/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Aleksander Morgado */ #include #include #include #include #include #include #include "ModemManager.h" #include "mm-serial-parsers.h" #include "mm-log.h" #include "mm-modem-helpers.h" #include "mm-errors-types.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-time.h" #include "mm-base-modem-at.h" #include "mm-bearer-list.h" #include "mm-broadband-bearer-icera.h" #include "mm-broadband-modem-icera.h" static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_time_init (MMIfaceModemTime *iface); static MMIfaceModem *iface_modem_parent; static MMIfaceModem3gpp *iface_modem_3gpp_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIcera, mm_broadband_modem_icera, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)) enum { PROP_0, PROP_DEFAULT_IP_METHOD, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBroadbandModemIceraPrivate { MMBearerIpMethod default_ip_method; GRegex *nwstate_regex; GRegex *pacsp_regex; GRegex *ipdpact_regex; /* Cache of the most recent value seen by the unsolicited message handler */ MMModemAccessTechnology last_act; }; /*****************************************************************************/ /* Load supported modes (Modem interface) */ static void add_supported_mode (GArray **combinations, guint mode) { MMModemModeCombination combination; switch (mode) { case 0: mm_dbg ("Modem supports 2G-only mode"); combination.allowed = MM_MODEM_MODE_2G; combination.preferred = MM_MODEM_MODE_NONE; break; case 1: mm_dbg ("Modem supports 3G-only mode"); combination.allowed = MM_MODEM_MODE_3G; combination.preferred = MM_MODEM_MODE_NONE; break; case 2: mm_dbg ("Modem supports 2G/3G mode with 2G preferred"); combination.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); combination.preferred = MM_MODEM_MODE_2G; break; case 3: mm_dbg ("Modem supports 2G/3G mode with 3G preferred"); combination.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); combination.preferred = MM_MODEM_MODE_3G; break; case 5: mm_dbg ("Modem supports 'any', but not explicitly listing it"); /* Any, no need to add it to the list */ return; default: mm_warn ("Unsupported Icera mode found: %u", mode); return; } if (*combinations == NULL) *combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5); g_array_append_val (*combinations, combination); } static GArray * load_supported_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { GArray *combinations = NULL; const gchar *response; gchar **split = NULL; GMatchInfo *match_info; GRegex *r; guint i; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; /* Reply goes like this: * AT%IPSYS=? * %IPSYS: (0-3,5),(0-3) */ r = g_regex_new ("\\%IPSYS:\\s*\\((.*)\\)\\s*,\\((.*)\\)", G_REGEX_RAW, 0, NULL); g_assert (r != NULL); g_regex_match (r, response, 0, &match_info); if (g_match_info_matches (match_info)) { gchar *aux; aux = mm_get_string_unquoted_from_match_info (match_info, 1); if (aux) { split = g_strsplit (aux, ",", -1); g_free (aux); } } g_match_info_free (match_info); g_regex_unref (r); if (!split) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%%IPSYS=? response didn't match"); g_regex_unref (r); return NULL; } for (i = 0; split[i]; i++) { gchar *interval_separator; g_strstrip (split[i]); interval_separator = strstr (split[i], "-"); if (interval_separator) { /* Add all in interval */ gchar *first, *last; guint modefirst, modelast; first = g_strdup (split[i]); interval_separator = strstr (first, "-"); *(interval_separator++) = '\0'; last = interval_separator; if (mm_get_uint_from_str (first, &modefirst) && mm_get_uint_from_str (last, &modelast) && modefirst < modelast && modelast <= 5) { guint j; for (j = modefirst; j <= modelast; j++) add_supported_mode (&combinations, j); } else mm_warn ("Couldn't parse mode interval (%s) in %%IPSYS=? response", split[i]); g_free (first); } else { guint mode; /* Add single */ if (mm_get_uint_from_str (split[i], &mode)) add_supported_mode (&combinations, mode); else mm_warn ("Couldn't parse mode (%s) in %%IPSYS=? response", split[i]); } } g_strfreev (split); if (!combinations) g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No mode combinations were parsed from the %%IPSYS=? response (%s)", response); return combinations; } static void load_supported_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "%IPSYS=?", 3, TRUE, callback, user_data); } /*****************************************************************************/ /* Load initial allowed/preferred modes (Modem interface) */ static gboolean modem_load_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, MMModemMode *allowed, MMModemMode *preferred, GError **error) { const gchar *response; const gchar *str; gint mode, domain; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return FALSE; str = mm_strip_tag (response, "%IPSYS:"); if (!sscanf (str, "%d,%d", &mode, &domain)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse %%IPSYS response: '%s'", response); return FALSE; } switch (mode) { case 0: *allowed = MM_MODEM_MODE_2G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 1: *allowed = MM_MODEM_MODE_3G; *preferred = MM_MODEM_MODE_NONE; return TRUE; case 2: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_2G; return TRUE; case 3: *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_3G; return TRUE; case 5: /* any */ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); *preferred = MM_MODEM_MODE_NONE; return TRUE; default: break; } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't parse unexpected %%IPSYS response: '%s'", response); return FALSE; } static void modem_load_current_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "%IPSYS?", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Set allowed modes (Modem interface) */ static gboolean modem_set_current_modes_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void allowed_mode_update_ready (MMBaseModem *self, GAsyncResult *res, GSimpleAsyncResult *operation_result) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) /* Let the error be critical. */ g_simple_async_result_take_error (operation_result, error); else g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); g_simple_async_result_complete (operation_result); g_object_unref (operation_result); } static void modem_set_current_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; gchar *command; gint icera_mode = -1; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_set_current_modes); /* * The core has checked the following: * - that 'allowed' are a subset of the 'supported' modes * - that 'preferred' is one mode, and a subset of 'allowed' */ if (allowed == MM_MODEM_MODE_2G) icera_mode = 0; else if (allowed == MM_MODEM_MODE_3G) icera_mode = 1; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { if (preferred == MM_MODEM_MODE_2G) icera_mode = 2; else if (preferred == MM_MODEM_MODE_3G) icera_mode = 3; else /* none preferred, so AUTO */ icera_mode = 5; } else if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) icera_mode = 5; if (icera_mode < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_simple_async_result_set_error (result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_free (allowed_str); g_free (preferred_str); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } command = g_strdup_printf ("%%IPSYS=%d", icera_mode); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 3, FALSE, (GAsyncReadyCallback)allowed_mode_update_ready, result); g_free (command); } /*****************************************************************************/ /* Icera-specific unsolicited events handling */ typedef struct { guint cid; MMBearerConnectionStatus status; } BearerListReportStatusForeachContext; static void bearer_list_report_status_foreach (MMBearer *bearer, BearerListReportStatusForeachContext *ctx) { if (mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (bearer)) != ctx->cid) return; if (!MM_IS_BROADBAND_BEARER_ICERA (bearer)) return; mm_bearer_report_connection_status (bearer, ctx->status); } static void ipdpact_received (MMAtSerialPort *port, GMatchInfo *match_info, MMBroadbandModemIcera *self) { MMBearerList *list = NULL; BearerListReportStatusForeachContext ctx; guint cid; guint status; /* Ensure we got proper parsed values */ if (!mm_get_uint_from_match_info (match_info, 1, &cid) || !mm_get_uint_from_match_info (match_info, 2, &status)) return; /* Setup context */ ctx.cid = cid; ctx.status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; switch (status) { case 0: ctx.status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED; break; case 1: ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTED; break; case 2: /* activating */ break; case 3: ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED; break; default: mm_warn ("Unknown Icera connect status %d", status); break; } /* If unknown status, don't try to report anything */ if (ctx.status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) return; /* If empty bearer list, nothing else to do */ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); if (!list) return; /* Will report status only in the bearer with the specific CID */ mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_report_status_foreach, &ctx); g_object_unref (list); } static MMModemAccessTechnology nwstate_to_act (const gchar *str) { /* small 'g' means CS, big 'G' means PS */ if (!strcmp (str, "2g")) return MM_MODEM_ACCESS_TECHNOLOGY_GSM; else if (!strcmp (str, "2G-GPRS")) return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; else if (!strcmp (str, "2G-EDGE")) return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; else if (!strcmp (str, "3G")) return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; else if (!strcmp (str, "3g")) return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; else if (!strcmp (str, "R99")) return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; else if (!strcmp (str, "3G-HSDPA") || !strcmp (str, "HSDPA")) return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; else if (!strcmp (str, "3G-HSUPA") || !strcmp (str, "HSUPA")) return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; else if (!strcmp (str, "3G-HSDPA-HSUPA") || !strcmp (str, "HSDPA-HSUPA")) return MM_MODEM_ACCESS_TECHNOLOGY_HSPA; else if (!strcmp (str, "3G-HSDPA-HSUPA-HSPA+") || !strcmp (str, "HSDPA-HSUPA-HSPA+")) return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static void nwstate_changed (MMAtSerialPort *port, GMatchInfo *info, MMBroadbandModemIcera *self) { gchar *str; /* * %NWSTATE: ,,,, * * shows the actual access technology in-use when a * PS connection is active. */ /* Process signal quality... */ str = g_match_info_fetch (info, 1); if (str) { gint rssi; rssi = atoi (str); rssi = CLAMP (rssi, 0, 5) * 100 / 5; g_free (str); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)rssi); } /* Process access technology... */ str = g_match_info_fetch (info, 4); if (!str || (strcmp (str, "-") == 0)) { g_free (str); str = g_match_info_fetch (info, 3); } if (str) { MMModemAccessTechnology act; act = nwstate_to_act (str); g_free (str); /* Cache last received value, needed for explicit access technology * query handling */ self->priv->last_act = act; mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, MM_MODEM_ACCESS_TECHNOLOGY_ANY); } } static void set_unsolicited_events_handlers (MMBroadbandModemIcera *self, gboolean enable) { MMAtSerialPort *ports[2]; guint i; ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); /* Enable unsolicited events in given port */ for (i = 0; i < 2; i++) { if (!ports[i]) continue; /* Access technology related */ mm_at_serial_port_add_unsolicited_msg_handler ( ports[i], self->priv->nwstate_regex, enable ? (MMAtSerialUnsolicitedMsgFn)nwstate_changed : NULL, enable ? self : NULL, NULL); /* Connection status related */ mm_at_serial_port_add_unsolicited_msg_handler ( ports[i], self->priv->ipdpact_regex, enable ? (MMAtSerialUnsolicitedMsgFn)ipdpact_received : NULL, enable ? self : NULL, NULL); /* Always to ignore */ if (!enable) { mm_at_serial_port_add_unsolicited_msg_handler ( ports[i], self->priv->pacsp_regex, NULL, NULL, NULL); } } } /*****************************************************************************/ /* Load access technologies (Modem interface) */ static gboolean modem_load_access_technologies_finish (MMIfaceModem *self, GAsyncResult *res, MMModemAccessTechnology *access_technologies, guint *mask, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return FALSE; *access_technologies = ((MMModemAccessTechnology) GPOINTER_TO_UINT ( g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (res)))); *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; return TRUE; } static void nwstate_query_ready (MMBroadbandModemIcera *self, GAsyncResult *res, GSimpleAsyncResult *operation_result) { GError *error = NULL; mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { mm_dbg ("Couldn't query access technology: '%s'", error->message); g_simple_async_result_take_error (operation_result, error); } else { /* * The unsolicited message handler will already have run and * removed the NWSTATE response, so we use the result from there. */ g_simple_async_result_set_op_res_gpointer (operation_result, GUINT_TO_POINTER (self->priv->last_act), NULL); } g_simple_async_result_complete (operation_result); g_object_unref (operation_result); } static void modem_load_access_technologies (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_load_access_technologies); mm_base_modem_at_command ( MM_BASE_MODEM (self), "%NWSTATE", 3, FALSE, (GAsyncReadyCallback)nwstate_query_ready, result); } /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ static gboolean modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error)) g_simple_async_result_take_error (simple, error); else { /* Our own setup now */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), TRUE); g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE); } g_simple_async_result_complete (simple); g_object_unref (simple); } static void modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's setup */ iface_modem_3gpp_parent->setup_unsolicited_events ( self, (GAsyncReadyCallback)parent_setup_unsolicited_events_ready, g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_3gpp_setup_unsolicited_events)); } static void parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error)) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE); g_simple_async_result_complete (simple); g_object_unref (simple); } static void modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_3gpp_cleanup_unsolicited_events); /* Our own cleanup first */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), FALSE); /* And now chain up parent's cleanup */ iface_modem_3gpp_parent->cleanup_unsolicited_events ( self, (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready, result); } /*****************************************************************************/ /* Enable/disable unsolicited events (3GPP interface) */ static gboolean modem_3gpp_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void own_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gboolean (simple, TRUE); g_simple_async_result_complete (simple); g_object_unref (simple); } static void parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) { g_simple_async_result_take_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); return; } /* Our own enable now */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "%NWSTATE=1", 3, FALSE, (GAsyncReadyCallback)own_enable_unsolicited_events_ready, simple); } static void modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { /* Chain up parent's enable */ iface_modem_3gpp_parent->enable_unsolicited_events ( self, (GAsyncReadyCallback)parent_enable_unsolicited_events_ready, g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_3gpp_enable_unsolicited_events)); } static void parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gboolean (simple, TRUE); g_simple_async_result_complete (simple); g_object_unref (simple); } static void own_disable_unsolicited_events_ready (MMIfaceModem3gpp *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { g_simple_async_result_take_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); return; } /* Next, chain up parent's disable */ iface_modem_3gpp_parent->disable_unsolicited_events ( MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)parent_disable_unsolicited_events_ready, simple); } static void modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "%NWSTATE=0", 3, FALSE, (GAsyncReadyCallback)own_disable_unsolicited_events_ready, g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_3gpp_disable_unsolicited_events)); } /*****************************************************************************/ /* Create bearer (Modem interface) */ static MMBearer * modem_create_bearer_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return NULL; return MM_BEARER (g_object_ref ( g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (res)))); } static void broadband_bearer_icera_new_ready (GObject *source, GAsyncResult *res, GSimpleAsyncResult *simple) { MMBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_icera_new_finish (res, &error); if (!bearer) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gpointer (simple, bearer, (GDestroyNotify)g_object_unref); g_simple_async_result_complete (simple); g_object_unref (simple); } static void broadband_bearer_new_ready (GObject *source, GAsyncResult *res, GSimpleAsyncResult *simple) { MMBearer *bearer = NULL; GError *error = NULL; bearer = mm_broadband_bearer_new_finish (res, &error); if (!bearer) g_simple_async_result_take_error (simple, error); else g_simple_async_result_set_op_res_gpointer (simple, bearer, (GDestroyNotify)g_object_unref); g_simple_async_result_complete (simple); g_object_unref (simple); } static void modem_create_bearer (MMIfaceModem *self, MMBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_create_bearer); /* If we get a NET port, create Icera bearer */ if (mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) { mm_broadband_bearer_icera_new ( MM_BROADBAND_MODEM (self), MM_BROADBAND_MODEM_ICERA (self)->priv->default_ip_method, properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_icera_new_ready, result); return; } /* Otherwise, plain generic broadband bearer */ mm_broadband_bearer_new ( MM_BROADBAND_MODEM (self), properties, NULL, /* cancellable */ (GAsyncReadyCallback)broadband_bearer_new_ready, result); } /*****************************************************************************/ /* Modem power up (Modem interface) */ static gboolean modem_power_up_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void cfun_enable_ready (MMBaseModem *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { /* Ignore all errors except NOT_ALLOWED, which means Airplane Mode */ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED)) g_simple_async_result_take_error (simple, error); else g_error_free (error); } g_simple_async_result_complete (simple); g_object_unref (simple); } static void modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_power_up); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=1", 10, FALSE, (GAsyncReadyCallback)cfun_enable_ready, result); } /*****************************************************************************/ /* Modem power down (Modem interface) */ static gboolean modem_power_down_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_power_down (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and * keeps access to the SIM */ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CFUN=4", /* The modem usually completes +CFUN=4 within 1-2 seconds, * but sometimes takes a ridiculously long time (~30-35 seconds). * It's better to have a long timeout here than to have the * modem not responding to subsequent AT commands until +CFUN=4 * completes. */ 40, FALSE, callback, user_data); } /*****************************************************************************/ /* Reset (Modem interface) */ static gboolean modem_reset_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); } static void modem_reset (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "%IRESET", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load unlock retries (Modem interface) */ static MMUnlockRetries * modem_load_unlock_retries_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return NULL; return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (res))); } static void load_unlock_retries_ready (MMBaseModem *self, GAsyncResult *res, GSimpleAsyncResult *operation_result) { const gchar *response; GError *error = NULL; int pin1, puk1, pin2, puk2; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { mm_dbg ("Couldn't query unlock retries: '%s'", error->message); g_simple_async_result_take_error (operation_result, error); g_simple_async_result_complete (operation_result); g_object_unref (operation_result); return; } response = mm_strip_tag (response, "%PINNUM:"); if (sscanf (response, " %d, %d, %d, %d", &pin1, &puk1, &pin2, &puk2) == 4) { MMUnlockRetries *retries; retries = mm_unlock_retries_new (); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2); mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2); g_simple_async_result_set_op_res_gpointer (operation_result, retries, (GDestroyNotify)g_object_unref); } else { g_simple_async_result_set_error (operation_result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid unlock retries response: '%s'", response); } g_simple_async_result_complete (operation_result); g_object_unref (operation_result); } static void modem_load_unlock_retries (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "%PINNUM?", 3, FALSE, (GAsyncReadyCallback)load_unlock_retries_ready, g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_load_unlock_retries)); } /*****************************************************************************/ /* Generic band handling utilities */ typedef struct { MMModemBand band; char *name; gboolean enabled; } Band; static void band_free (Band *b) { g_free (b->name); g_free (b); } static const Band modem_bands[] = { /* Sort 3G first since it's preferred */ { MM_MODEM_BAND_U2100, "FDD_BAND_I", FALSE }, { MM_MODEM_BAND_U1900, "FDD_BAND_II", FALSE }, { MM_MODEM_BAND_U1800, "FDD_BAND_III", FALSE }, { MM_MODEM_BAND_U17IV, "FDD_BAND_IV", FALSE }, { MM_MODEM_BAND_U800, "FDD_BAND_VI", FALSE }, { MM_MODEM_BAND_U850, "FDD_BAND_V", FALSE }, { MM_MODEM_BAND_U900, "FDD_BAND_VIII", FALSE }, /* 2G second */ { MM_MODEM_BAND_G850, "G850", FALSE }, { MM_MODEM_BAND_DCS, "DCS", FALSE }, { MM_MODEM_BAND_EGSM, "EGSM", FALSE }, { MM_MODEM_BAND_PCS, "PCS", FALSE }, /* And ANY last since it's most inclusive */ { MM_MODEM_BAND_ANY, "ANY", FALSE }, }; static const guint modem_band_any_bit = 1 << (G_N_ELEMENTS (modem_bands) - 1); static MMModemBand icera_band_to_mm (const char *icera) { int i; for (i = 0 ; i < G_N_ELEMENTS (modem_bands); i++) { if (g_strcmp0 (icera, modem_bands[i].name) == 0) return modem_bands[i].band; } return MM_MODEM_BAND_UNKNOWN; } static GSList * parse_bands (const gchar *response, guint32 *out_len) { GRegex *r; GMatchInfo *info; GSList *bands = NULL; g_return_val_if_fail (out_len != NULL, NULL); /* * Response is a number of lines of the form: * "EGSM": 0 * "FDD_BAND_I": 1 * ... * with 1 and 0 indicating whether the particular band is enabled or not. */ r = g_regex_new ("^\"(\\w+)\": (\\d)", G_REGEX_MULTILINE, G_REGEX_MATCH_NEWLINE_ANY, NULL); g_assert (r != NULL); g_regex_match (r, response, 0, &info); while (g_match_info_matches (info)) { gchar *name, *enabled; Band *b; MMModemBand band; name = g_match_info_fetch (info, 1); enabled = g_match_info_fetch (info, 2); band = icera_band_to_mm (name); if (band != MM_MODEM_BAND_UNKNOWN) { b = g_malloc0 (sizeof (Band)); b->band = band; b->name = g_strdup (name); b->enabled = (enabled[0] == '1' ? TRUE : FALSE); bands = g_slist_append (bands, b); *out_len = *out_len + 1; } g_free (name); g_free (enabled); g_match_info_next (info, NULL); } g_match_info_free (info); g_regex_unref (r); return bands; } /*****************************************************************************/ /* Load supported bands (Modem interface) */ typedef struct { MMBaseModemAtCommand *cmds; GSList *check_bands; GSList *enabled_bands; guint32 idx; } SupportedBandsContext; static void supported_bands_context_free (SupportedBandsContext *ctx) { guint i; for (i = 0; ctx->cmds[i].command; i++) g_free (ctx->cmds[i].command); g_free (ctx->cmds); g_slist_free_full (ctx->check_bands, (GDestroyNotify) band_free); g_slist_free_full (ctx->enabled_bands, (GDestroyNotify) band_free); g_free (ctx); } static GArray * modem_load_supported_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return NULL; return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (res))); } static void load_supported_bands_ready (MMBaseModem *self, GAsyncResult *res, GSimpleAsyncResult *simple) { GError *error = NULL; SupportedBandsContext *ctx = NULL; GArray *bands; GSList *iter; mm_base_modem_at_sequence_finish (self, res, (gpointer) &ctx, &error); if (error) g_simple_async_result_take_error (simple, error); else { bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), ctx->idx); /* Add already enabled bands */ for (iter = ctx->enabled_bands; iter; iter = g_slist_next (iter)) { Band *b = iter->data; g_array_prepend_val (bands, b->band); } /* Add any checked bands that are supported */ for (iter = ctx->check_bands; iter; iter = g_slist_next (iter)) { Band *b = iter->data; /* 'enabled' here really means supported/unsupported */ if (b->enabled) g_array_prepend_val (bands, b->band); } g_simple_async_result_set_op_res_gpointer (simple, bands, (GDestroyNotify) g_array_unref); } g_simple_async_result_complete (simple); g_object_unref (simple); } static gboolean load_supported_bands_response_processor (MMBaseModem *self, gpointer context, const gchar *command, const gchar *response, gboolean last_command, const GError *error, GVariant **result, GError **result_error) { SupportedBandsContext *ctx = context; Band *b = g_slist_nth_data (ctx->check_bands, ctx->idx++); /* If there was no error setting the band, that band is supported. We * abuse the 'enabled' item to mean supported/unsupported. */ b->enabled = !error; /* Continue to next band */ return FALSE; } static void load_supported_bands_get_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GSimpleAsyncResult *operation_result) { SupportedBandsContext *ctx; const gchar *response; GError *error = NULL; GSList *iter, *new; guint32 len = 0, i = 0; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { mm_dbg ("Couldn't query current bands: '%s'", error->message); g_simple_async_result_take_error (operation_result, error); g_simple_async_result_complete (operation_result); g_object_unref (operation_result); return; } ctx = g_new0 (SupportedBandsContext, 1); /* For each reported band, build up an AT command to set that band * to its current enabled/disabled state. */ iter = ctx->check_bands = parse_bands (response, &len); ctx->cmds = g_new0 (MMBaseModemAtCommand, len + 1); while (iter) { Band *b = iter->data; if (b->enabled || b->band == MM_MODEM_BAND_ANY) { /* Move known-supported band to the enabled list */ new = g_slist_next (iter); ctx->check_bands = g_slist_remove_link (ctx->check_bands, iter); ctx->enabled_bands = g_slist_prepend (ctx->enabled_bands, iter->data); g_slist_free (iter); iter = new; } else { /* Check support for disabled band */ ctx->cmds[i].command = g_strdup_printf ("%%IPBM=\"%s\",0", b->name); ctx->cmds[i].timeout = 10; ctx->cmds[i].allow_cached = FALSE; ctx->cmds[i].response_processor = load_supported_bands_response_processor; i++; iter = g_slist_next (iter); } } mm_base_modem_at_sequence (MM_BASE_MODEM (self), ctx->cmds, ctx, (GDestroyNotify) supported_bands_context_free, (GAsyncReadyCallback) load_supported_bands_ready, operation_result); } static void modem_load_supported_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { /* The modems report some bands as disabled that they don't actually * support enabling. Thanks Icera! So we have to try setting each * band to it's current enabled/disabled value, and the modem will * return an error if it doesn't support that band at all. */ mm_base_modem_at_command ( MM_BASE_MODEM (self), "%IPBM?", 3, FALSE, (GAsyncReadyCallback)load_supported_bands_get_current_bands_ready, g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_load_supported_bands)); } /*****************************************************************************/ /* Load current bands (Modem interface) */ static GArray * modem_load_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return NULL; return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( G_SIMPLE_ASYNC_RESULT (res))); } static void load_current_bands_ready (MMIfaceModem *self, GAsyncResult *res, GSimpleAsyncResult *operation_result) { GArray *bands; const gchar *response; GError *error = NULL; GSList *parsed, *iter; guint32 len = 0; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (!response) { mm_dbg ("Couldn't query current bands: '%s'", error->message); g_simple_async_result_take_error (operation_result, error); g_simple_async_result_complete (operation_result); g_object_unref (operation_result); } else { /* Parse bands from Icera response into MM band numbers */ parsed = parse_bands (response, &len); bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), len); for (iter = parsed; iter; iter = g_slist_next (iter)) { Band *b = iter->data; if (b->enabled) g_array_append_val (bands, b->band); } g_slist_free_full (parsed, (GDestroyNotify) band_free); g_simple_async_result_set_op_res_gpointer (operation_result, bands, (GDestroyNotify)g_array_unref); g_simple_async_result_complete (operation_result); g_object_unref (operation_result); } } static void modem_load_current_bands (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command ( MM_BASE_MODEM (self), "%IPBM?", 3, FALSE, (GAsyncReadyCallback)load_current_bands_ready, g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_load_current_bands)); } /*****************************************************************************/ /* Set current bands (Modem interface) */ typedef struct { GSimpleAsyncResult *result; guint bandbits; guint enablebits; guint disablebits; } SetCurrentBandsContext; /* * The modem's band-setting command (%IPBM=) enables or disables one * band at a time, and one band must always be enabled. Here, we first * get the set of enabled bands, compute the difference between that * set and the requested set, enable any added bands, and finally * disable any removed bands. */ static gboolean modem_set_current_bands_finish (MMIfaceModem *self, GAsyncResult *res, GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void set_one_band (MMIfaceModem *self, SetCurrentBandsContext *ctx); static void set_current_bands_context_complete_and_free (SetCurrentBandsContext *ctx) { g_simple_async_result_complete (ctx->result); g_object_unref (ctx->result); g_slice_free (SetCurrentBandsContext, ctx); } static void set_current_bands_next (MMIfaceModem *self, GAsyncResult *res, SetCurrentBandsContext *ctx) { GError *error = NULL; if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { mm_dbg ("Couldn't set current bands: '%s'", error->message); g_simple_async_result_take_error (ctx->result, error); set_current_bands_context_complete_and_free (ctx); return; } set_one_band (self, ctx); } static void set_one_band (MMIfaceModem *self, SetCurrentBandsContext *ctx) { guint enable, band; gchar *command; /* Find the next band to enable or disable, always doing enables first */ enable = 1; band = ffs (ctx->enablebits); if (band == 0) { enable = 0; band = ffs (ctx->disablebits); } if (band == 0) { /* Both enabling and disabling are done */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); set_current_bands_context_complete_and_free (ctx); return; } /* Note that ffs() returning 2 corresponds to 1 << 1, not 1 << 2 */ band--; mm_dbg("1. enablebits %x disablebits %x band %d enable %d", ctx->enablebits, ctx->disablebits, band, enable); if (enable) ctx->enablebits &= ~(1 << band); else ctx->disablebits &= ~(1 << band); mm_dbg("2. enablebits %x disablebits %x", ctx->enablebits, ctx->disablebits); command = g_strdup_printf ("%%IPBM=\"%s\",%d", modem_bands[band].name, enable); mm_base_modem_at_command ( MM_BASE_MODEM (self), command, 10, FALSE, (GAsyncReadyCallback)set_current_bands_next, ctx); g_free (command); } static guint band_array_to_bandbits (GArray *bands) { MMModemBand band; guint i, j, bandbits; bandbits = 0; for (i = 0 ; i < bands->len ; i++) { band = g_array_index (bands, MMModemBand, i); for (j = 0 ; j < G_N_ELEMENTS (modem_bands) ; j++) { if (modem_bands[j].band == band) { bandbits |= 1 << j; break; } } g_assert (j < G_N_ELEMENTS (modem_bands)); } return bandbits; } static void set_current_bands_got_current_bands (MMIfaceModem *self, GAsyncResult *res, SetCurrentBandsContext *ctx) { GArray *bands; GError *error = NULL; guint currentbits; bands = modem_load_current_bands_finish (self, res, &error); if (!bands) { g_simple_async_result_take_error (ctx->result, error); set_current_bands_context_complete_and_free (ctx); return; } currentbits = band_array_to_bandbits (bands); ctx->enablebits = ctx->bandbits & ~currentbits; ctx->disablebits = currentbits & ~ctx->bandbits; set_one_band (self, ctx); } static void modem_set_current_bands (MMIfaceModem *self, GArray *bands_array, GAsyncReadyCallback callback, gpointer user_data) { SetCurrentBandsContext *ctx; ctx = g_slice_new0 (SetCurrentBandsContext); ctx->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_set_current_bands); ctx->bandbits = band_array_to_bandbits (bands_array); /* * If ANY is requested, simply enable ANY to activate all bands except for * those forbidden. */ if (ctx->bandbits & modem_band_any_bit) { ctx->enablebits = modem_band_any_bit; ctx->disablebits = 0; set_one_band (self, ctx); return; } modem_load_current_bands (self, (GAsyncReadyCallback)set_current_bands_got_current_bands, ctx); } /*****************************************************************************/ /* Load network timezone (Time interface) */ static gboolean parse_tlts_query_reply (const gchar *response, gchar **iso8601, MMNetworkTimezone **tz, GError **error) { gint year; gint month; gint day; gint hour; gint minute; gint second; gchar sign; gint offset; GDateTime *utc, *adjusted; /* TLTS reports UTC time with the TZ offset to *local* time */ response = mm_strip_tag (response, "*TLTS: "); if (sscanf (response, "\"%02d/%02d/%02d,%02d:%02d:%02d%c%02d\"", &year, &month, &day, &hour, &minute, &second, &sign, &offset) != 8) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown *TLTS response: %s", response); return FALSE; } /* Icera modems only report a 2-digit year, while ISO-8601 requires * a 4-digit year. Assume 2000. */ if (year < 100) year += 2000; /* Offset comes in 15-min units */ offset *= 15; /* Apply sign to offset; */ if (sign == '-') offset *= -1; utc = g_date_time_new_utc (year, month, day, hour, minute, second); if (!utc) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid *TLTS date/time: %s", response); return FALSE; } /* Convert UTC time to local time by adjusting by the timezone offset */ adjusted = g_date_time_add_minutes (utc, offset); g_date_time_unref (utc); if (!adjusted) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to convert modem time to local time (offset %d)", offset); return FALSE; } /* Convert offset from minutes-to-UTC to minutes-from-UTC */ offset *= -1; if (tz) { *tz = mm_network_timezone_new (); mm_network_timezone_set_offset (*tz, offset); } if (iso8601) { *iso8601 = mm_new_iso8601_time (g_date_time_get_year (adjusted), g_date_time_get_month (adjusted), g_date_time_get_day_of_month (adjusted), g_date_time_get_hour (adjusted), g_date_time_get_minute (adjusted), g_date_time_get_second (adjusted), TRUE, offset); } g_date_time_unref (adjusted); return TRUE; } static MMNetworkTimezone * modem_time_load_network_timezone_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; MMNetworkTimezone *tz; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL); if (!response) { /* We'll assume we can retry a bit later */ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Retry"); return NULL; } return (parse_tlts_query_reply (response, NULL, &tz, error) ? tz : NULL); } static void modem_time_load_network_timezone (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*TLTS", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Load network time (Time interface) */ static gchar * modem_time_load_network_time_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { const gchar *response; gchar *iso8601; response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); if (!response) return NULL; return (parse_tlts_query_reply (response, &iso8601, NULL, error) ? iso8601 : NULL); } static void modem_time_load_network_time (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { mm_base_modem_at_command (MM_BASE_MODEM (self), "*TLTS", 3, FALSE, callback, user_data); } /*****************************************************************************/ /* Check support (Time interface) */ static gboolean modem_time_check_support_finish (MMIfaceModemTime *self, GAsyncResult *res, GError **error) { /* We assume Icera devices always support *TLTS, since they appear * to return ERROR if the modem is not powered up, and thus we cannot * check for *TLTS support during modem initialization. */ return TRUE; } static void modem_time_check_support (MMIfaceModemTime *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, modem_time_check_support); g_simple_async_result_complete_in_idle (result); g_object_unref (result); } /*****************************************************************************/ /* Setup ports (Broadband modem class) */ static void setup_ports (MMBroadbandModem *self) { /* Call parent's setup ports first always */ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_icera_parent_class)->setup_ports (self); /* Now reset the unsolicited messages we'll handle when enabled */ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), FALSE); } /*****************************************************************************/ MMBroadbandModemIcera * mm_broadband_modem_icera_new (const gchar *device, const gchar **drivers, const gchar *plugin, guint16 vendor_id, guint16 product_id) { return g_object_new (MM_TYPE_BROADBAND_MODEM_ICERA, MM_BASE_MODEM_DEVICE, device, MM_BASE_MODEM_DRIVERS, drivers, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandModemIcera *self = MM_BROADBAND_MODEM_ICERA (object); switch (prop_id) { case PROP_DEFAULT_IP_METHOD: self->priv->default_ip_method = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MMBroadbandModemIcera *self = MM_BROADBAND_MODEM_ICERA (object); switch (prop_id) { case PROP_DEFAULT_IP_METHOD: g_value_set_enum (value, self->priv->default_ip_method); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_modem_icera_init (MMBroadbandModemIcera *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BROADBAND_MODEM_ICERA, MMBroadbandModemIceraPrivate); self->priv->nwstate_regex = g_regex_new ("%NWSTATE:\\s*(-?\\d+),(\\d+),([^,]*),([^,]*),(\\d+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->pacsp_regex = g_regex_new ("\\r\\n\\+PACSP(\\d)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->ipdpact_regex = g_regex_new ("\\r\\n%IPDPACT:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); self->priv->default_ip_method = MM_BEARER_IP_METHOD_STATIC; self->priv->last_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; } static void finalize (GObject *object) { MMBroadbandModemIcera *self = MM_BROADBAND_MODEM_ICERA (object); g_regex_unref (self->priv->nwstate_regex); g_regex_unref (self->priv->pacsp_regex); g_regex_unref (self->priv->ipdpact_regex); G_OBJECT_CLASS (mm_broadband_modem_icera_parent_class)->finalize (object); } static void iface_modem_init (MMIfaceModem *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); iface->load_supported_modes = load_supported_modes; iface->load_supported_modes_finish = load_supported_modes_finish; iface->load_current_modes = modem_load_current_modes; iface->load_current_modes_finish = modem_load_current_modes_finish; iface->set_current_modes = modem_set_current_modes; iface->set_current_modes_finish = modem_set_current_modes_finish; iface->load_access_technologies = modem_load_access_technologies; iface->load_access_technologies_finish = modem_load_access_technologies_finish; iface->load_unlock_retries = modem_load_unlock_retries; iface->load_unlock_retries_finish = modem_load_unlock_retries_finish; iface->load_supported_bands = modem_load_supported_bands; iface->load_supported_bands_finish = modem_load_supported_bands_finish; iface->load_current_bands = modem_load_current_bands; iface->load_current_bands_finish = modem_load_current_bands_finish; iface->modem_power_up = modem_power_up; iface->modem_power_up_finish = modem_power_up_finish; /* Note: don't implement modem_init_power_down, as CFUN=4 here may take * looong to reply */ iface->modem_power_down = modem_power_down; iface->modem_power_down_finish = modem_power_down_finish; iface->reset = modem_reset; iface->reset_finish = modem_reset_finish; iface->set_current_bands = modem_set_current_bands; iface->set_current_bands_finish = modem_set_current_bands_finish; iface->create_bearer = modem_create_bearer; iface->create_bearer_finish = modem_create_bearer_finish; } static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events; iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events; iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish; iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events; iface->enable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events; iface->disable_unsolicited_events_finish = modem_3gpp_enable_disable_unsolicited_events_finish; } 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 mm_broadband_modem_icera_class_init (MMBroadbandModemIceraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandModemIceraPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; broadband_modem_class->setup_ports = setup_ports; properties[PROP_DEFAULT_IP_METHOD] = g_param_spec_enum (MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, "Default IP method", "Default IP Method (static or DHCP) to use.", MM_TYPE_BEARER_IP_METHOD, MM_BEARER_IP_METHOD_STATIC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEFAULT_IP_METHOD, properties[PROP_DEFAULT_IP_METHOD]); }