aboutsummaryrefslogtreecommitdiff
path: root/plugins/mm-modem-hso.c
diff options
context:
space:
mode:
authorTambet Ingo <tambet@gmail.com>2008-08-26 14:40:20 +0300
committerTambet Ingo <tambet@gmail.com>2008-08-26 14:40:20 +0300
commit64b4827c4c2e6fbb68682d249cfb642650789e81 (patch)
tree482a1c38fd8ab242d45e3d36b68a8099232439e5 /plugins/mm-modem-hso.c
parentfc992ce8abe8e7c8b07b5a0645348073af0cc708 (diff)
Implement HSO modem driver.
Diffstat (limited to 'plugins/mm-modem-hso.c')
-rw-r--r--plugins/mm-modem-hso.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c
new file mode 100644
index 00000000..0153d816
--- /dev/null
+++ b/plugins/mm-modem-hso.c
@@ -0,0 +1,500 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <dbus/dbus-glib.h>
+#include "mm-modem-hso.h"
+#include "mm-serial.h"
+#include "mm-gsm-modem.h"
+#include "mm-modem-error.h"
+#include "mm-callback-info.h"
+
+static void impl_hso_get_ip4_config (MMModemHso *self, DBusGMethodInvocation *context);
+static void impl_hso_authenticate (MMModemHso *self,
+ const char *username,
+ const char *password,
+ DBusGMethodInvocation *context);
+
+#include "mm-gsm-modem-hso-glue.h"
+
+static gpointer mm_modem_hso_parent_class = NULL;
+
+#define MM_MODEM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HSO, MMModemHsoPrivate))
+
+typedef struct {
+ char *network_device;
+ gboolean authenticated;
+} MMModemHsoPrivate;
+
+enum {
+ PROP_0,
+ PROP_NETWORK_DEVICE,
+
+ LAST_PROP
+};
+
+#define OWANDATA_TAG "_OWANDATA: "
+
+MMModem *
+mm_modem_hso_new (const char *serial_device,
+ const char *network_device,
+ const char *driver)
+{
+ g_return_val_if_fail (serial_device != NULL, NULL);
+ g_return_val_if_fail (network_device != NULL, NULL);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ return MM_MODEM (g_object_new (MM_TYPE_MODEM_HSO,
+ MM_SERIAL_DEVICE, serial_device,
+ MM_MODEM_DRIVER, driver,
+ MM_MODEM_HSO_NETWORK_DEVICE, network_device,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+need_auth_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemFn callback;
+
+ if (!MM_MODEM_HSO_GET_PRIVATE (modem)->authenticated)
+ /* Re-use the PIN_NEEDED error as HSO never needs PIN or PUK, right? */
+ error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_PIN_NEEDED, "%s", "Authentication needed.");
+
+ callback = mm_callback_info_get_data (info, "callback");
+ callback (MM_MODEM (modem),
+ error,
+ mm_callback_info_get_data (info, "user-data"));
+}
+
+static void
+need_auth (MMModem *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, need_auth_done, NULL);
+ info->user_data = info;
+ mm_callback_info_set_data (info, "callback", callback, NULL);
+ mm_callback_info_set_data (info, "user-data", user_data, NULL);
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+call_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* Success */
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed.");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+clear_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ char *command;
+ char *responses[] = { "_OWANCALL: ", "ERROR", NULL };
+ guint id = 0;
+
+ /* FIXME: Ignore errors here? */
+ /* Try to connect */
+ command = g_strdup_printf ("AT_OWANCALL=%d,1,1", mm_generic_gsm_get_cid (MM_GENERIC_GSM (serial)));
+ if (mm_serial_send_command_string (serial, command))
+ id = mm_serial_wait_for_reply (serial, 10, responses, responses, call_done, user_data);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+do_connect (MMModem *modem,
+ const char *number,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *command;
+ char *responses[] = { "_OWANCALL: ", "ERROR", "NO CARRIER", NULL };
+ guint id = 0;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+
+ /* Kill any existing connection first */
+ command = g_strdup_printf ("AT_OWANCALL=%d,0,1", mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem)));
+ if (mm_serial_send_command_string (MM_SERIAL (modem), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (modem), 5, responses, responses, clear_done, user_data);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Dial failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+free_dns_array (gpointer data)
+{
+ g_array_free ((GArray *) data, TRUE);
+}
+
+static void
+ip4_callback_wrapper (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMModemHsoIp4Fn callback;
+
+ callback = mm_callback_info_get_data (info, "callback");
+ callback (MM_MODEM_HSO (modem),
+ GPOINTER_TO_UINT (mm_callback_info_get_data (info, "ip4-address")),
+ mm_callback_info_get_data (info, "ip4-dns"),
+ error,
+ mm_callback_info_get_data (info, "user-data"));
+}
+
+static void
+get_ip4_config_done (MMSerial *serial, const char *response, gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ char **items, **iter;
+ GArray *dns_array;
+ int i;
+ guint32 tmp;
+ guint cid;
+
+ if (!response || strncmp (response, OWANDATA_TAG, strlen (OWANDATA_TAG))) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s",
+ "Retrieving failed: invalid response.");
+ goto out;
+ }
+
+ cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (serial));
+ dns_array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 2);
+ items = g_strsplit (response + strlen (OWANDATA_TAG), ", ", 0);
+
+ for (iter = items, i = 0; *iter; iter++, i++) {
+ if (i == 0) { /* CID */
+ long int tmp;
+
+ errno = 0;
+ tmp = strtol (*iter, NULL, 10);
+ if (errno != 0 || tmp < 0 || (guint) tmp != cid) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Unknown CID in OWANDATA response (got %d, expected %d)", (guint) tmp, cid);
+ break;
+ }
+ } else if (i == 1) { /* IP address */
+ if (inet_pton (AF_INET, *iter, &tmp) > 0)
+ mm_callback_info_set_data (info, "ip4-address", GUINT_TO_POINTER (tmp), NULL);
+ } else if (i == 3) { /* DNS 1 */
+ if (inet_pton (AF_INET, *iter, &tmp) > 0)
+ g_array_append_val (dns_array, tmp);
+ } else if (i == 4) { /* DNS 2 */
+ if (inet_pton (AF_INET, *iter, &tmp) > 0)
+ g_array_append_val (dns_array, tmp);
+ }
+ }
+
+ g_strfreev (items);
+ mm_callback_info_set_data (info, "ip4-dns", dns_array, free_dns_array);
+
+ out:
+ mm_callback_info_schedule (info);
+}
+
+void
+mm_hso_modem_get_ip4_config (MMModemHso *self,
+ MMModemHsoIp4Fn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *command;
+ const char terminators[] = { '\r', '\n', '\0' };
+ guint id = 0;
+
+ g_return_if_fail (MM_IS_MODEM_HSO (self));
+ g_return_if_fail (callback != NULL);
+
+ info = mm_callback_info_new (MM_MODEM (self), ip4_callback_wrapper, NULL);
+ info->user_data = info;
+ mm_callback_info_set_data (info, "callback", callback, NULL);
+ mm_callback_info_set_data (info, "user-data", user_data, NULL);
+
+ command = g_strdup_printf ("AT_OWANDATA=%d", mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)));
+ if (mm_serial_send_command_string (MM_SERIAL (self), command))
+ id = mm_serial_get_reply (MM_SERIAL (self), 5, terminators, get_ip4_config_done, info);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Retrieving failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+impl_hso_ip4_config_done (MMModemHso *modem,
+ guint32 address,
+ GArray *dns,
+ 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, address, dns);
+}
+
+static void
+impl_hso_get_ip4_config (MMModemHso *self,
+ DBusGMethodInvocation *context)
+{
+ mm_hso_modem_get_ip4_config (self, impl_hso_ip4_config_done, context);
+}
+
+static void
+auth_done (MMSerial *serial,
+ int reply_index,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ switch (reply_index) {
+ case 0:
+ /* success */
+ MM_MODEM_HSO_GET_PRIVATE (serial)->authenticated = TRUE;
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Authentication failed");
+ break;
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+void
+mm_hso_modem_authenticate (MMModemHso *self,
+ const char *username,
+ const char *password,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *command;
+ char *responses[] = { "OK", "ERROR", NULL };
+ guint id = 0;
+
+ g_return_if_fail (MM_IS_MODEM_HSO (self));
+ g_return_if_fail (callback != NULL);
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ command = g_strdup_printf ("AT$QCPDPP=%d,1,\"%s\",\"%s\"",
+ mm_generic_gsm_get_cid (MM_GENERIC_GSM (self)),
+ password ? password : "",
+ username ? username : "");
+
+ if (mm_serial_send_command_string (MM_SERIAL (self), command))
+ id = mm_serial_wait_for_reply (MM_SERIAL (self), 5, responses, responses, auth_done, user_data);
+
+ g_free (command);
+
+ if (!id) {
+ info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "%s", "Authentication failed.");
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+impl_hso_auth_done (MMModem *modem,
+ 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);
+}
+
+static void
+impl_hso_authenticate (MMModemHso *self,
+ const char *username,
+ const char *password,
+ DBusGMethodInvocation *context)
+{
+ mm_hso_modem_authenticate (self, username, password, impl_hso_auth_done, context);
+}
+
+/*****************************************************************************/
+
+static void
+modem_init (MMModem *modem_class)
+{
+ modem_class->connect = do_connect;
+}
+
+static void
+gsm_modem_init (MMGsmModem *gsm_modem_class)
+{
+ gsm_modem_class->need_authentication = need_auth;
+}
+
+static void
+mm_modem_hso_init (MMModemHso *self)
+{
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ MMModemHsoPrivate *priv;
+
+ object = G_OBJECT_CLASS (mm_modem_hso_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ priv = MM_MODEM_HSO_GET_PRIVATE (object);
+
+ if (!priv->network_device) {
+ g_warning ("No network device provided");
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return object;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NETWORK_DEVICE:
+ /* Construct only */
+ priv->network_device = g_value_dup_string (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)
+{
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NETWORK_DEVICE:
+ g_value_set_string (value, priv->network_device);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+static void
+finalize (GObject *object)
+{
+ MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (object);
+
+ g_free (priv->network_device);
+
+ G_OBJECT_CLASS (mm_modem_hso_parent_class)->finalize (object);
+}
+
+static void
+mm_modem_hso_class_init (MMModemHsoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ mm_modem_hso_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (object_class, sizeof (MMModemHsoPrivate));
+
+ /* Virtual methods */
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ /* Properties */
+ g_object_class_install_property
+ (object_class, PROP_NETWORK_DEVICE,
+ g_param_spec_string (MM_MODEM_HSO_NETWORK_DEVICE,
+ "NetworkDevice",
+ "Network device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GType
+mm_modem_hso_get_type (void)
+{
+ static GType modem_hso_type = 0;
+
+ if (G_UNLIKELY (modem_hso_type == 0)) {
+ static const GTypeInfo modem_hso_type_info = {
+ sizeof (MMModemHsoClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) mm_modem_hso_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (MMModemHso),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) mm_modem_hso_init,
+ };
+
+ static const GInterfaceInfo modem_iface_info = {
+ (GInterfaceInitFunc) modem_init
+ };
+
+ static const GInterfaceInfo gsm_modem_iface_info = {
+ (GInterfaceInitFunc) gsm_modem_init
+ };
+
+ modem_hso_type = g_type_register_static (MM_TYPE_GENERIC_GSM, "MMModemHso", &modem_hso_type_info, 0);
+
+ g_type_add_interface_static (modem_hso_type, MM_TYPE_MODEM, &modem_iface_info);
+ g_type_add_interface_static (modem_hso_type, MM_TYPE_GSM_MODEM, &gsm_modem_iface_info);
+ }
+
+ return modem_hso_type;
+}