Introduce the Modem driver API
authorDenis Kenzior <denkenz@gmail.com>
Sat, 22 Aug 2009 00:11:32 +0000 (19:11 -0500)
committerDenis Kenzior <denkenz@gmail.com>
Sat, 22 Aug 2009 00:25:51 +0000 (19:25 -0500)
include/modem.h
include/types.h
src/modem.c

index b021c6c..0a5ef60 100644 (file)
@@ -26,6 +26,8 @@
 extern "C" {
 #endif
 
+#include <ofono/types.h>
+
 struct ofono_modem;
 
 void ofono_modem_add_interface(struct ofono_modem *modem,
@@ -39,8 +41,24 @@ const char *ofono_modem_get_path(struct ofono_modem *modem);
 void ofono_modem_set_data(struct ofono_modem *modem, void *data);
 void *ofono_modem_get_data(struct ofono_modem *modem);
 
-struct ofono_modem *ofono_modem_register();
-int ofono_modem_unregister(struct ofono_modem *modem);
+struct ofono_modem *ofono_modem_create(const char *node, const char *type);
+int ofono_modem_register(struct ofono_modem *modem);
+void ofono_modem_remove(struct ofono_modem *modem);
+
+void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
+ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
+
+struct ofono_modem_driver {
+       const char *name;
+       int (*probe)(struct ofono_modem *modem);
+       int (*remove)(struct ofono_modem *modem);
+       int (*enable)(struct ofono_modem *modem);
+       int (*disable)(struct ofono_modem *modem);
+       int (*populate)(struct ofono_modem *modem);
+};
+
+int ofono_modem_driver_register(const struct ofono_modem_driver *);
+void ofono_modem_driver_unregister(const struct ofono_modem_driver *);
 
 #ifdef __cplusplus
 }
index 7f99f80..6a9681d 100644 (file)
 extern "C" {
 #endif
 
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef int            ofono_bool_t;
+
 /* MCC is always three digits. MNC is either two or three digits */
 #define OFONO_MAX_MCC_LENGTH 3
 #define OFONO_MAX_MNC_LENGTH 3
index aa701bf..a9693c6 100644 (file)
 #include "common.h"
 
 static GSList *g_devinfo_drivers = NULL;
-
+static GSList *g_driver_list = NULL;
 static GSList *g_modem_list = NULL;
-static int g_next_modem_id = 1;
 
 struct ofono_modem {
-       int             id;
        char            *path;
        GSList          *atoms;
        GSList          *atom_watches;
        int             next_atom_watch_id;
        GSList          *interface_list;
-       int             flags;
        unsigned int    call_ids;
        DBusMessage     *pending;
        guint           interface_update;
+       ofono_bool_t    powered;
+       ofono_bool_t    powered_pending;
+       ofono_bool_t    powered_persistent;
+       guint           timeout;
+       const struct ofono_modem_driver *driver;
        void            *driver_data;
+       char            *driver_type;
 };
 
 struct ofono_devinfo {
@@ -355,26 +358,6 @@ static void remove_all_atoms(struct ofono_modem *modem)
        modem->atoms = NULL;
 }
 
-static void modem_free(gpointer data)
-{
-       struct ofono_modem *modem = data;
-
-       if (modem == NULL)
-               return;
-
-       g_slist_foreach(modem->interface_list, (GFunc)g_free, NULL);
-       g_slist_free(modem->interface_list);
-
-       if (modem->pending)
-               dbus_message_unref(modem->pending);
-
-       if (modem->interface_update)
-               g_source_remove(modem->interface_update);
-
-       g_free(modem->path);
-       g_free(modem);
-}
-
 static DBusMessage *modem_get_properties(DBusConnection *conn,
                                                DBusMessage *msg, void *data)
 {
@@ -397,6 +380,9 @@ static DBusMessage *modem_get_properties(DBusConnection *conn,
                                        OFONO_PROPERTIES_ARRAY_SIGNATURE,
                                        &dict);
 
+       ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN,
+                               &modem->powered);
+
        devinfo_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_DEVINFO);
 
        /* We cheat a little here and don't check the registered status */
@@ -440,8 +426,120 @@ static DBusMessage *modem_get_properties(DBusConnection *conn,
        return reply;
 }
 
+static int set_powered(struct ofono_modem *modem, ofono_bool_t powered)
+{
+       const struct ofono_modem_driver *driver = modem->driver;
+       int err = -EINVAL;
+
+       if (driver == NULL)
+               return -EINVAL;
+
+       if (modem->powered_pending == powered)
+               return -EALREADY;
+
+       modem->powered_pending = powered;
+
+       if (powered == TRUE) {
+               if (driver->enable)
+                       err = driver->enable(modem);
+       } else {
+               if (driver->disable)
+                       err = driver->disable(modem);
+       }
+
+       return err;
+}
+
+static gboolean set_powered_timeout(gpointer user)
+{
+       struct ofono_modem *modem = user;
+
+       DBG("modem: %p", modem);
+
+       modem->timeout = 0;
+
+       if (modem->pending != NULL) {
+               DBusMessage *reply;
+
+               reply = __ofono_error_timed_out(modem->pending);
+               __ofono_dbus_pending_reply(&modem->pending, reply);
+       }
+
+       return FALSE;
+}
+
+static DBusMessage *modem_set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       DBusMessageIter iter, var;
+       const char *name;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_str_equal(name, "Powered") == TRUE) {
+               ofono_bool_t powered;
+               int err;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &powered);
+
+               if (modem->pending != NULL)
+                       return __ofono_error_busy(msg);
+
+               if (modem->powered == powered)
+                       return dbus_message_new_method_return(msg);
+
+               err = set_powered(modem, powered);
+               if (err < 0) {
+                       if (err != -EINPROGRESS)
+                               return __ofono_error_failed(msg);
+
+                       modem->pending = dbus_message_ref(msg);
+                       modem->timeout = g_timeout_add_seconds(20,
+                                               set_powered_timeout, modem);
+                       return NULL;
+               }
+
+               modem->powered = powered;
+               g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+               ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Powered", DBUS_TYPE_BOOLEAN,
+                                               &powered);
+
+               if (powered) {
+                       if (modem->driver->populate)
+                               modem->driver->populate(modem);
+               } else {
+                       remove_all_atoms(modem);
+               }
+
+               return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
 static GDBusMethodTable modem_methods[] = {
        { "GetProperties",      "",     "a{sv}",        modem_get_properties },
+       { "SetProperty",        "sv",   "",             modem_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
        { }
 };
 
@@ -450,6 +548,50 @@ static GDBusSignalTable modem_signals[] = {
        { }
 };
 
+void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t dbus_powered;
+
+       if (modem->timeout > 0) {
+               g_source_remove(modem->timeout);
+               modem->timeout = 0;
+       }
+
+       if (modem->pending != NULL) {
+               DBusMessage *reply;
+
+               if (powered == modem->powered_pending)
+                       reply = dbus_message_new_method_return(modem->pending);
+               else
+                       reply = __ofono_error_failed(modem->pending);
+
+               __ofono_dbus_pending_reply(&modem->pending, reply);
+       }
+
+       if (modem->powered == powered)
+               return;
+
+       modem->powered = powered;
+       modem->powered_pending = powered;
+
+       if (modem->driver == NULL)
+               return;
+
+       dbus_powered = powered;
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Powered", DBUS_TYPE_BOOLEAN,
+                                               &dbus_powered);
+
+       if (powered) {
+               if (modem->driver->populate)
+                       modem->driver->populate(modem);
+       } else {
+               remove_all_atoms(modem);
+       }
+}
+
 static gboolean trigger_interface_update(void *data)
 {
        struct ofono_modem *modem = data;
@@ -655,7 +797,6 @@ void ofono_devinfo_driver_unregister(const struct ofono_devinfo_driver *d)
 static void devinfo_remove(struct ofono_atom *atom)
 {
        struct ofono_devinfo *info = __ofono_atom_get_data(atom);
-
        DBG("atom: %p", atom);
 
        if (info == NULL)
@@ -724,120 +865,199 @@ void *ofono_devinfo_get_data(struct ofono_devinfo *info)
        return info->driver_data;
 }
 
-static struct ofono_modem *modem_create(int id)
+/* Clients only need to free *modems */
+const char **__ofono_modem_get_list()
 {
-       char path[128];
-       DBusConnection *conn = ofono_dbus_get_connection();
+       GSList *l;
+       int i;
        struct ofono_modem *modem;
+       const char **modems;
+
+       modems = g_new0(const char *, g_slist_length(g_modem_list) + 1);
+
+       for (l = g_modem_list, i = 0; l; l = l->next, i++) {
+               modem = l->data;
+
+               if (modem->driver == NULL)
+                       continue;
+
+               modems[i] = modem->path;
+       }
+
+       return modems;
+}
+
+struct ofono_modem *ofono_modem_create(const char *node, const char *type)
+{
+       struct ofono_modem *modem;
+       char path[128];
+
+       if (strlen(node) > 16)
+               return NULL;
 
        modem = g_try_new0(struct ofono_modem, 1);
+
        if (modem == NULL)
                return modem;
 
-       modem->id = id;
-
-       snprintf(path, sizeof(path), "/modem%d", modem->id);
+       snprintf(path, sizeof(path), "/%s", node);
        modem->path = g_strdup(path);
+       modem->driver_type = g_strdup(type);
 
-       if (!g_dbus_register_interface(conn, path, OFONO_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;
-       }
+       g_modem_list = g_slist_prepend(g_modem_list, modem);
 
        return modem;
 }
 
-static void modem_remove(struct ofono_modem *modem)
+static void emit_modems()
 {
        DBusConnection *conn = ofono_dbus_get_connection();
-       /* Need to make a copy to keep gdbus happy */
-       char *path = g_strdup(modem->path);
-
-       ofono_debug("Removing modem: %s", modem->path);
+       const char **modems = __ofono_modem_get_list();
 
-       remove_all_atoms(modem);
-       remove_all_watches(modem);
+       if (modems == NULL)
+               return;
 
-       g_dbus_unregister_interface(conn, path, OFONO_MODEM_INTERFACE);
+       ofono_dbus_signal_array_property_changed(conn,
+                               OFONO_MANAGER_PATH,
+                               OFONO_MANAGER_INTERFACE, "Modems",
+                               DBUS_TYPE_OBJECT_PATH, &modems);
 
-       g_free(path);
+       g_free(modems);
 }
 
-/* Clients only need to free *modems */
-const char **__ofono_modem_get_list()
+int ofono_modem_register(struct ofono_modem *modem)
 {
+       DBusConnection *conn = ofono_dbus_get_connection();
        GSList *l;
-       int i;
-       struct ofono_modem *modem;
-       const char **modems;
 
-       modems = g_new0(const char *, g_slist_length(g_modem_list) + 1);
+       if (modem == NULL)
+               return -EINVAL;
 
-       for (l = g_modem_list, i = 0; l; l = l->next, i++) {
-               modem = l->data;
+       if (modem->driver != NULL)
+               return -EALREADY;
 
-               modems[i] = modem->path;
+       for (l = g_driver_list; l; l = l->next) {
+               const struct ofono_modem_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, modem->driver_type))
+                       continue;
+
+               if (drv->probe(modem) < 0)
+                       continue;
+
+               modem->driver = drv;
+               break;
        }
 
-       return modems;
+       if (modem->driver == NULL)
+               return -ENODEV;
+
+       if (!g_dbus_register_interface(conn, modem->path, OFONO_MODEM_INTERFACE,
+                               modem_methods, modem_signals, NULL,
+                               modem, NULL)) {
+               ofono_error("Modem register failed on path %s", modem->path);
+
+               if (modem->driver->remove)
+                       modem->driver->remove(modem);
+
+               modem->driver = NULL;
+
+               return -EIO;
+       }
+
+       __ofono_history_probe_drivers(modem);
+
+       g_free(modem->driver_type);
+       modem->driver_type = NULL;
+
+       emit_modems();
+
+       /* TODO: Read powered property from store */
+       if (modem->powered_persistent)
+               set_powered(modem, TRUE);
+
+       if (modem->powered == TRUE && modem->driver->populate)
+               modem->driver->populate(modem);
+
+       return 0;
 }
 
-struct ofono_modem *ofono_modem_register()
+static void modem_unregister(struct ofono_modem *modem)
 {
-       struct ofono_modem *modem;
        DBusConnection *conn = ofono_dbus_get_connection();
-       const char **modems;
-
-       modem = modem_create(g_next_modem_id);
 
-       if (modem == NULL)
-               return 0;
+       if (modem->driver == NULL)
+               return;
 
-       ++g_next_modem_id;
+       remove_all_atoms(modem);
+       remove_all_watches(modem);
 
-       __ofono_history_probe_drivers(modem);
-       g_modem_list = g_slist_prepend(g_modem_list, modem);
+       g_slist_foreach(modem->interface_list, (GFunc)g_free, NULL);
+       g_slist_free(modem->interface_list);
+       modem->interface_list = NULL;
 
-       modems = __ofono_modem_get_list();
+       if (modem->timeout) {
+               g_source_remove(modem->timeout);
+               modem->timeout = 0;
+       }
 
-       if (modems) {
-               ofono_dbus_signal_array_property_changed(conn,
-                               OFONO_MANAGER_PATH,
-                               OFONO_MANAGER_INTERFACE, "Modems",
-                               DBUS_TYPE_OBJECT_PATH, &modems);
+       if (modem->pending) {
+               dbus_message_unref(modem->pending);
+               modem->pending = NULL;
+       }
 
-               g_free(modems);
+       if (modem->interface_update) {
+               g_source_remove(modem->interface_update);
+               modem->interface_update = 0;
        }
 
-       return modem;
+       g_dbus_unregister_interface(conn, modem->path, OFONO_MODEM_INTERFACE);
+
+       emit_modems();
+
+       if (modem->powered == TRUE)
+               set_powered(modem, FALSE);
+
+       if (modem->driver->remove)
+               modem->driver->remove(modem);
+
+       modem->driver = NULL;
 }
 
-int ofono_modem_unregister(struct ofono_modem *m)
+void ofono_modem_remove(struct ofono_modem *modem)
 {
-       struct ofono_modem *modem = m;
-       DBusConnection *conn = ofono_dbus_get_connection();
-       const char **modems;
+       DBG("%p", modem);
 
        if (modem == NULL)
-               return -1;
+               return;
 
-       modem_remove(modem);
+       if (modem->driver)
+               modem_unregister(modem);
 
        g_modem_list = g_slist_remove(g_modem_list, modem);
 
-       modems = __ofono_modem_get_list();
+       if (modem->driver_type)
+               g_free(modem->driver_type);
 
-       if (modems) {
-               ofono_dbus_signal_array_property_changed(conn,
-                               OFONO_MANAGER_PATH,
-                               OFONO_MANAGER_INTERFACE, "Modems",
-                               DBUS_TYPE_OBJECT_PATH, &modems);
+       g_free(modem->path);
+       g_free(modem);
+}
 
-               g_free(modems);
-       }
+int ofono_modem_driver_register(const struct ofono_modem_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_driver_list = g_slist_prepend(g_driver_list, (void *)d);
 
        return 0;
 }
+
+void ofono_modem_driver_unregister(const struct ofono_modem_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_driver_list = g_slist_remove(g_driver_list, (void *)d);
+}