summaryrefslogtreecommitdiffstats
path: root/src/modem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modem.c')
-rw-r--r--src/modem.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/src/modem.c b/src/modem.c
new file mode 100644
index 00000000..0b8b36ef
--- /dev/null
+++ b/src/modem.c
@@ -0,0 +1,432 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "dbus-gsm.h"
+#include "modem.h"
+#include "driver.h"
+
+#define MODEM_INTERFACE "org.ofono.Modem"
+
+#define MODEM_FLAG_INITIALIZING_ATTRS 1
+
+#define ATTRIBUTE_QUERY_DELAY 0
+
+struct ofono_modem_data {
+ char *manufacturer;
+ char *model;
+ char *revision;
+ char *serial;
+ GSList *interface_list;
+ int flags;
+ unsigned int idlist;
+ struct ofono_modem_attribute_ops *ops;
+ DBusMessage *pending;
+ guint interface_update;
+};
+
+unsigned int modem_alloc_callid(struct ofono_modem *modem)
+{
+ struct ofono_modem_data *d = modem->modem_info;
+ unsigned int i;
+
+ for (i = 1; i < sizeof(d->idlist) * 8; i++) {
+ if (d->idlist & (0x1 << i))
+ continue;
+
+ d->idlist |= (0x1 << i);
+ return i;
+ }
+
+ return 0;
+}
+
+void modem_release_callid(struct ofono_modem *modem, int id)
+{
+ struct ofono_modem_data *d = modem->modem_info;
+
+ d->idlist &= ~(0x1 << id);
+}
+
+void ofono_modem_set_userdata(struct ofono_modem *modem, void *userdata)
+{
+ if (modem)
+ modem->userdata = userdata;
+}
+
+void *ofono_modem_userdata(struct ofono_modem *modem)
+{
+ if (modem)
+ return modem->userdata;
+
+ return NULL;
+}
+
+static void modem_free(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ GSList *l;
+
+ if (modem == NULL)
+ return;
+
+ for (l = modem->modem_info->interface_list; l; l = l->next)
+ g_free(l->data);
+
+ g_slist_free(modem->modem_info->interface_list);
+
+ g_free(modem->modem_info->manufacturer);
+ g_free(modem->modem_info->serial);
+ g_free(modem->modem_info->revision);
+ g_free(modem->modem_info->model);
+
+ if (modem->modem_info->pending)
+ dbus_message_unref(modem->modem_info->pending);
+
+ if (modem->modem_info->interface_update)
+ g_source_remove(modem->modem_info->interface_update);
+
+ g_free(modem->modem_info);
+ g_free(modem->path);
+ g_free(modem);
+}
+
+static DBusMessage *generate_properties_reply(struct ofono_modem *modem,
+ DBusConnection *conn, DBusMessage *msg)
+{
+ struct ofono_modem_data *info = modem->modem_info;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ char **interfaces;
+ int i;
+ GSList *l;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ if (info->manufacturer)
+ dbus_gsm_dict_append(&dict, "Manufacturer", DBUS_TYPE_STRING,
+ &info->manufacturer);
+
+ if (info->model)
+ dbus_gsm_dict_append(&dict, "Model", DBUS_TYPE_STRING,
+ &info->model);
+
+ if (info->revision)
+ dbus_gsm_dict_append(&dict, "Revision", DBUS_TYPE_STRING,
+ &info->revision);
+
+ if (info->serial)
+ dbus_gsm_dict_append(&dict, "Serial", DBUS_TYPE_STRING,
+ &info->serial);
+
+ interfaces = g_new0(char *, g_slist_length(info->interface_list) + 1);
+ for (i = 0, l = info->interface_list; l; l = l->next, i++)
+ interfaces[i] = l->data;
+
+ dbus_gsm_dict_append_array(&dict, "Interfaces", DBUS_TYPE_STRING,
+ &interfaces);
+
+ g_free(interfaces);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *modem_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (modem->modem_info->flags & MODEM_FLAG_INITIALIZING_ATTRS) {
+ modem->modem_info->pending = dbus_message_ref(msg);
+ return NULL;
+ }
+
+ return generate_properties_reply(modem, conn, msg);
+}
+
+static GDBusMethodTable modem_methods[] = {
+ { "GetProperties", "", "a{sv}", modem_get_properties,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable modem_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static gboolean trigger_interface_update(void *data)
+{
+ struct ofono_modem *modem = data;
+ struct ofono_modem_data *info = modem->modem_info;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ char **interfaces;
+ GSList *l;
+ int i;
+
+ interfaces = g_new0(char *, g_slist_length(info->interface_list) + 1);
+ for (i = 0, l = info->interface_list; l; l = l->next, i++)
+ interfaces[i] = l->data;
+
+ dbus_gsm_signal_array_property_changed(conn, modem->path,
+ MODEM_INTERFACE,
+ "Interfaces", DBUS_TYPE_STRING,
+ &interfaces);
+
+ g_free(interfaces);
+
+ info->interface_update = 0;
+
+ return FALSE;
+}
+
+void modem_add_interface(struct ofono_modem *modem, const char *interface)
+{
+ struct ofono_modem_data *info = modem->modem_info;
+
+ info->interface_list =
+ g_slist_prepend(info->interface_list, g_strdup(interface));
+
+ if (info->interface_update == 0)
+ info->interface_update =
+ g_timeout_add(0, trigger_interface_update, modem);
+}
+
+void modem_remove_interface(struct ofono_modem *modem, const char *interface)
+{
+ struct ofono_modem_data *info = modem->modem_info;
+
+ GSList *found = g_slist_find_custom(info->interface_list,
+ interface,
+ (GCompareFunc) strcmp);
+
+ if (!found) {
+ ofono_error("Interface %s not found on the interface_list",
+ interface);
+ return;
+ }
+
+ g_free(found->data);
+
+ info->interface_list =
+ g_slist_remove(info->interface_list, found->data);
+
+ if (info->interface_update == 0)
+ info->interface_update =
+ g_timeout_add(0, trigger_interface_update, modem);
+}
+
+static void finish_attr_query(struct ofono_modem *modem)
+{
+ //struct ofono_modem_data *info = modem->modem_info;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ modem->modem_info->flags &= ~MODEM_FLAG_INITIALIZING_ATTRS;
+
+ if (!modem->modem_info->pending)
+ return;
+
+ reply = generate_properties_reply(modem, conn,
+ modem->modem_info->pending);
+
+ if (reply)
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(modem->modem_info->pending);
+ modem->modem_info->pending = NULL;
+}
+
+static void query_serial_cb(const struct ofono_error *error,
+ const char *serial, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->serial = g_strdup(serial);
+
+ finish_attr_query(modem);
+}
+
+static gboolean query_serial(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_serial) {
+ finish_attr_query(modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_serial(modem, query_serial_cb, modem);
+
+ return FALSE;
+}
+
+static void query_revision_cb(const struct ofono_error *error,
+ const char *revision, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->revision = g_strdup(revision);
+
+ g_timeout_add(0, query_serial, modem);
+}
+
+static gboolean query_revision(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_revision) {
+ g_timeout_add(0, query_serial, modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_revision(modem, query_revision_cb, modem);
+
+ return FALSE;
+}
+
+static void query_model_cb(const struct ofono_error *error,
+ const char *model, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->model = g_strdup(model);
+
+ g_timeout_add(0, query_revision, modem);
+}
+
+static gboolean query_model(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_model) {
+ /* If model is not supported, don't bother querying revision */
+ g_timeout_add(0, query_serial, modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_model(modem, query_model_cb, modem);
+
+ return FALSE;
+}
+
+static void query_manufacturer_cb(const struct ofono_error *error,
+ const char *manufacturer, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->manufacturer = g_strdup(manufacturer);
+
+ g_timeout_add(0, query_model, modem);
+}
+
+static gboolean query_manufacturer(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_manufacturer) {
+ g_timeout_add(0, query_model, modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_manufacturer(modem, query_manufacturer_cb,
+ modem);
+
+ return FALSE;
+}
+
+struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops)
+{
+ char path[MAX_DBUS_PATH_LEN];
+ DBusConnection *conn = dbus_gsm_connection();
+ struct ofono_modem *modem;
+
+ modem = g_try_new0(struct ofono_modem, 1);
+ if (modem == NULL)
+ return modem;
+
+ modem->modem_info = g_try_new0(struct ofono_modem_data, 1);
+ if (modem->modem_info == NULL) {
+ g_free(modem);
+ return NULL;
+ }
+
+ modem->id = id;
+ modem->modem_info->ops = ops;
+
+ snprintf(path, MAX_DBUS_PATH_LEN, "/modem%d", modem->id);
+ modem->path = g_strdup(path);
+
+ if (!g_dbus_register_interface(conn, path, MODEM_INTERFACE,
+ modem_methods, modem_signals, NULL,
+ modem, modem_free)) {
+ ofono_error("Modem interface init failed on path %s", path);
+ modem_free(modem);
+ return NULL;
+ }
+
+ modem->modem_info->flags |= MODEM_FLAG_INITIALIZING_ATTRS;
+ g_timeout_add(ATTRIBUTE_QUERY_DELAY, query_manufacturer, modem);
+
+ return modem;
+}
+
+void modem_remove(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ /* Need to make a copy to keep gdbus happy */
+ char *path = g_strdup(modem->path);
+
+ ofono_debug("Removing modem: %s", modem->path);
+
+ g_dbus_unregister_interface(conn, path, MODEM_INTERFACE);
+
+ g_free(path);
+}