summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-05-23 00:44:46 -0700
committerMarcel Holtmann <marcel@holtmann.org>2009-05-23 00:47:48 -0700
commit00d98e92bf5976a721caa68fc100fcc19068410b (patch)
treeb5c0bad35771bdf7094795efa582a690eaa5f92a
parent2fdc294cf3c903f2c292a7abaf4ab63ab37a20ac (diff)
downloadconnman-00d98e92bf5976a721caa68fc100fcc19068410b.tar.gz
connman-00d98e92bf5976a721caa68fc100fcc19068410b.tar.bz2
connman-00d98e92bf5976a721caa68fc100fcc19068410b.zip
Add plugin for Intel WiMAX SDK
-rwxr-xr-xbootstrap-configure2
-rw-r--r--configure.ac22
-rw-r--r--plugins/Makefile.am12
-rw-r--r--plugins/iwmx.c587
-rw-r--r--plugins/iwmx.h171
-rw-r--r--plugins/iwmxsdk.c990
6 files changed, 1743 insertions, 41 deletions
diff --git a/bootstrap-configure b/bootstrap-configure
index ec0100ac..6e5bfdc1 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -32,8 +32,8 @@ fi
--enable-hso \
--enable-ppp \
--enable-udev \
+ --enable-iwmx \
--enable-iospm \
- --enable-iwmxsdk \
--enable-polkit \
--enable-client \
--enable-tools $*
diff --git a/configure.ac b/configure.ac
index a8246054..c9a43360 100644
--- a/configure.ac
+++ b/configure.ac
@@ -208,20 +208,26 @@ AC_SUBST(UDEV_CFLAGS)
AC_SUBST(UDEV_LIBS)
AM_CONDITIONAL(UDEV, test "${enable_udev}" = "yes")
-AC_ARG_ENABLE(iospm, AC_HELP_STRING([--enable-iospm],
- [enable Intel OSPM support]), [enable_iospm=${enableval}])
-AM_CONDITIONAL(IOSPM, test "${enable_iospm}" = "yes")
+AC_ARG_WITH(iwmxsdk, AC_HELP_STRING([--with-iwmxsdk=PATH],
+ [path to Intel WiMAX SDK]),
+ [pkgconfig_iwmxsdk=${withval}/lib/pkgconfig])
-AC_ARG_ENABLE(iwmxsdk, AC_HELP_STRING([--enable-iwmxsdk],
- [enable Intel WiMAX support]), [enable_iwmxsdk=${enableval}])
-if (test "${enable_iwmxsdk}" = "yes"); then
+AC_ARG_ENABLE(iwmx, AC_HELP_STRING([--enable-iwmx],
+ [enable Intel WiMAX support]), [enable_iwmx=${enableval}])
+if (test "${enable_iwmx}" = "yes"); then
enable_threads="yes"
- PKG_CHECK_MODULES(IWMXSDK, libiWmxSdk-0, enable_iwmxsdk=yes,
+ export PKG_CONFIG_PATH="${pkgconfig_iwmxsdk}"
+ PKG_CHECK_MODULES(IWMXSDK, libiWmxSdk-0, enable_iwmx=yes,
AC_MSG_ERROR(Intel WiMAX SDK is required))
+ PKG_CONFIG_PATH=""
AC_SUBST(IWMXSDK_CFLAGS)
AC_SUBST(IWMXSDK_LIBS)
fi
-AM_CONDITIONAL(IWMXSDK, test "${enable_iwmxsdk}" = "yes")
+AM_CONDITIONAL(IWMX, test "${enable_iwmx}" = "yes")
+
+AC_ARG_ENABLE(iospm, AC_HELP_STRING([--enable-iospm],
+ [enable Intel OSPM support]), [enable_iospm=${enableval}])
+AM_CONDITIONAL(IOSPM, test "${enable_iospm}" = "yes")
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
AC_MSG_ERROR(GLib >= 2.16 is required))
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 6ffdde15..07535f74 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -141,17 +141,17 @@ policy_DATA = connman.policy
endif
endif
-if IOSPM
-plugin_LTLIBRARIES += iospm.la
-endif
-
-if IWMXSDK
+if IWMX
plugin_LTLIBRARIES += iwmxsdk.la
-iwmxsdk_la_SOURCES = iwmxsdk.c
+iwmxsdk_la_SOURCES = iwmx.h iwmx.c iwmxsdk.c
iwmxsdk_la_LIBADD = @IWMXSDK_LIBS@ @GLIB_LIBS@
iwmxsdk_la_CFLAGS = $(AM_CFLAGS) @IWMXSDK_CFLAGS@
endif
+if IOSPM
+plugin_LTLIBRARIES += iospm.la
+endif
+
if FAKE
plugin_LTLIBRARIES += fake.la
endif
diff --git a/plugins/iwmx.c b/plugins/iwmx.c
new file mode 100644
index 00000000..ccbd1d21
--- /dev/null
+++ b/plugins/iwmx.c
@@ -0,0 +1,587 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2007-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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/log.h>
+
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "iwmx.h"
+
+/*
+ * Connman plugin interface
+ *
+ * This part deals with the connman internals
+ */
+
+/* WiMAX network driver probe/remove, nops */
+static int iwmx_cm_network_probe(struct connman_network *nw)
+{
+ return 0;
+}
+
+static void iwmx_cm_network_remove(struct connman_network *nw)
+{
+}
+
+/*
+ * Called by connman when it wants us to tell the device to connect to
+ * the network @network_el; the device is @network_el->parent.
+ *
+ * We do a synchronous call to start the connection; the logic
+ * attached to the status change callback will update the connman
+ * internals once the change happens.
+ */
+static int iwmx_cm_network_connect(struct connman_network *nw)
+{
+ int result;
+ struct wmxsdk *wmxsdk;
+ const char *station_name = connman_network_get_identifier(nw);
+
+ wmxsdk = connman_device_get_data(connman_network_get_device(nw));
+ result = iwmx_sdk_connect(wmxsdk, nw);
+ DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
+ return result;
+}
+
+/*
+ * Called by connman to have the device @nw->parent
+ * disconnected from @nw.
+ *
+ * We do a synchronous call to start the disconnection; the logic
+ * attached to the status change callback will update the connman
+ * internals once the change happens.
+ */
+static int iwmx_cm_network_disconnect(struct connman_network *nw)
+{
+ int result;
+ struct wmxsdk *wmxsdk;
+ const char *station_name = connman_network_get_identifier(nw);
+
+ wmxsdk = connman_device_get_data(connman_network_get_device(nw));
+ result = iwmx_sdk_disconnect(wmxsdk);
+ DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
+ return 0;
+}
+
+/*
+ * "Driver" for the networks detected by a device.
+ */
+static struct connman_network_driver iwmx_cm_network_driver = {
+ .name = "iwmx",
+ .type = CONNMAN_NETWORK_TYPE_WIMAX,
+ .probe = iwmx_cm_network_probe,
+ .remove = iwmx_cm_network_remove,
+ .connect = iwmx_cm_network_connect,
+ .disconnect = iwmx_cm_network_disconnect,
+};
+
+/*
+ * A (maybe) new network is available, create/update its data
+ *
+ * If the network is new, we create and register a new element; if it
+ * is not, we reuse the one in the list.
+ *
+ * NOTE:
+ * wmxsdk->network_mutex has to be locked
+ */
+struct connman_network *__iwmx_cm_network_available(
+ struct wmxsdk *wmxsdk, const char *station_name,
+ const char *station_type,
+ const void *sdk_nspname, size_t sdk_nspname_size,
+ int strength)
+{
+ struct connman_network *nw = NULL;
+ struct connman_device *dev = wmxsdk->dev;
+ char group[3 * strlen(station_name) + 1];
+ unsigned cnt;
+
+ nw = connman_device_get_network(dev, station_name);
+ if (nw == NULL) {
+ DBG("new network %s", station_name);
+ nw = connman_network_create(station_name,
+ CONNMAN_NETWORK_TYPE_WIMAX);
+ connman_network_set_index(nw, connman_device_get_index(dev));
+ connman_network_set_protocol(nw, CONNMAN_NETWORK_PROTOCOL_IP);
+ connman_network_set_name(nw, station_name);
+ connman_network_set_blob(nw, "WiMAX.NSP.name",
+ sdk_nspname, sdk_nspname_size);
+ /* FIXME: add roaming info? */
+ /* Set the group name -- this has to be a unique
+ * [a-zA-Z0-9_] string common to all the networks that
+ * are actually the same provider. In WiMAX each
+ * network from the CAPI is a single provider, so we
+ * just set this as the network name, encoded in
+ * hex. */
+ for (cnt = 0; station_name[cnt] != 0; cnt++)
+ sprintf(group + 3 * cnt, "%02x", station_name[cnt]);
+ group[3 * cnt + 1] = 0;
+ connman_network_set_group(nw, station_name);
+ if (connman_device_add_network(dev, nw) < 0) {
+ connman_network_unref(nw);
+ goto error_add;
+ }
+ } else
+ DBG("updating network %s nw %p\n", station_name, nw);
+ connman_network_set_available(nw, TRUE);
+ connman_network_set_strength(nw, strength);
+ connman_network_set_string(nw, "WiMAX Network Type", station_type);
+error_add:
+ return nw;
+}
+
+/*
+ * A new network is available [locking version]
+ *
+ * See __iwmx_cm_network_available() for docs
+ */
+struct connman_network *iwmx_cm_network_available(
+ struct wmxsdk *wmxsdk, const char *station_name,
+ const char *station_type,
+ const void *sdk_nspname, size_t sdk_nspname_size,
+ int strength)
+{
+ struct connman_network *nw;
+
+ g_static_mutex_lock(&wmxsdk->network_mutex);
+ nw = __iwmx_cm_network_available(wmxsdk, station_name, station_type,
+ sdk_nspname, sdk_nspname_size,
+ strength);
+ g_static_mutex_unlock(&wmxsdk->network_mutex);
+ return nw;
+}
+
+/*
+ * The device has been enabled, make sure connman knows
+ */
+static void iwmx_cm_dev_enabled(struct wmxsdk *wmxsdk)
+{
+ struct connman_device *dev = wmxsdk->dev;
+ connman_inet_ifup(connman_device_get_index(dev));
+ connman_device_set_powered(dev, TRUE);
+}
+
+/*
+ * The device has been disabled, make sure connman is aware of it.
+ */
+static void iwmx_cm_dev_disabled(struct wmxsdk *wmxsdk)
+{
+ struct connman_device *dev = wmxsdk->dev;
+ connman_inet_ifdown(connman_device_get_index(dev));
+ connman_device_set_powered(dev, FALSE);
+}
+
+/*
+ * The device has been (externally to connman) connnected to a
+ * network, make sure connman knows.
+ *
+ * When the device is connected to a network, this function is called
+ * to change connman's internal state to reflect the fact.
+ *
+ * If the change came from an external entity, that means that our
+ * connect code wasn't called. Our connect code sets
+ * @wmxsdk->connecting_nw to the network we were connecting
+ * to. If it is unset, it means an external entity forced the device
+ * to connect. In that case, we need to find out which network it was
+ * connected to, and create/lookup a @nw for it.
+ *
+ * Once the nw is set, then we are done.
+ */
+static void iwmx_cm_dev_connected(struct wmxsdk *wmxsdk)
+{
+ struct connman_network *nw;
+
+ g_mutex_lock(wmxsdk->connect_mutex);
+ nw = wmxsdk->connecting_nw;
+ if (nw == NULL) {
+ nw = __iwmx_sdk_get_connected_network(wmxsdk);
+ if (nw == NULL) {
+ connman_error("wmxsdk: can't find connected network\n");
+ goto error_nw_find;
+ }
+ }
+ wmxsdk->nw = connman_network_ref(nw);
+ wmxsdk->connecting_nw = NULL;
+ connman_network_set_connected(nw, TRUE);
+ DBG("connected to network %s\n",
+ connman_network_get_identifier(nw));
+error_nw_find:
+ g_mutex_unlock(wmxsdk->connect_mutex);
+}
+
+/*
+ * The device has been (externally to connman) disconnnected, make
+ * sure connman knows
+ *
+ * We need to reverse the steps done in iwmx_cm_dev_connected().
+ * If the event was caused by an external entity and we had no record
+ * of being connected to a network...well, bad luck. We'll just
+ * pretend it happened ok.
+ */
+static void __iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
+{
+ struct connman_network *nw = wmxsdk->nw;
+
+ if (nw != NULL) {
+ DBG("disconnected from network %s\n",
+ connman_network_get_identifier(nw));
+ connman_network_set_connected(nw, FALSE);
+ connman_network_unref(nw);
+ wmxsdk->nw = NULL;
+ } else
+ DBG("disconnected from unknown network\n");
+}
+
+/*
+ * The device has been disconnnected, make sure connman knows
+ *
+ * See __iwmx_cm_dev_disconnect() for more information.
+ */
+static void iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
+{
+ g_mutex_lock(wmxsdk->connect_mutex);
+ __iwmx_cm_dev_disconnected(wmxsdk);
+ g_mutex_unlock(wmxsdk->connect_mutex);
+}
+
+/*
+ * Handle a change in state
+ *
+ * This is were most of the action happens. When the device changes
+ * state, this will catch it (through the state change callback or an
+ * explicit call) and call iwmx_cm_dev_*ed() to indicate to connman what
+ * happened.
+ *
+ * Finally, cache the new device status.
+ */
+void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+ WIMAX_API_DEVICE_STATUS __new_status)
+{
+ WIMAX_API_DEVICE_STATUS __old_status = wmxsdk->status;
+ WIMAX_API_DEVICE_STATUS old_status;
+ WIMAX_API_DEVICE_STATUS new_status;
+
+ /*
+ * Simplify state transition computations.
+ *
+ * For practical effects, some states are the same
+ */
+
+ /* Conection_Idle is the same as Data_Connected */
+ if (__old_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+ old_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
+ else
+ old_status = __old_status;
+ if (__new_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+ new_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
+ else
+ new_status = __new_status;
+
+ /* Radio off: all are just RF_OFF_SW (the highest) */
+ switch (__old_status) {
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ old_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
+ break;
+ default:
+ old_status = __old_status;
+ break;
+ }
+
+ switch (__new_status) {
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ new_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
+ break;
+ default:
+ new_status = __new_status;
+ break;
+ }
+
+ /* If no real state change, do nothing */
+ if (old_status == new_status) {
+ DBG("no state changed\n");
+ return;
+ } else
+ DBG("state change from %d (%d: %s) to %d (%d: %s)\n",
+ old_status, __old_status,
+ iwmx_sdk_dev_status_to_str(__old_status),
+ new_status, __new_status,
+ iwmx_sdk_dev_status_to_str(__new_status));
+
+ /* Cleanup old state */
+ switch (old_status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ /* This means the plugin is starting but the device is
+ * in some state already, so we need to update our
+ * internal knowledge of it. */
+ if (new_status > WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
+ iwmx_cm_dev_enabled(wmxsdk);
+ break;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ /* This means the radio is being turned on, so enable
+ * the device ( unless going to uninitialized). */
+ if (new_status != WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
+ iwmx_cm_dev_enabled(wmxsdk);
+ break;
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ iwmx_cm_dev_disconnected(wmxsdk);
+ break;
+ default:
+ connman_error("wmxsdk: unknown old status %d\n", old_status);
+ return;
+ };
+
+ /* Implement new state */
+ switch (new_status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ break;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ /* This means the radio is being turned off, so
+ * disable the device unless coming from uninitialized. */
+ if (old_status != WIMAX_API_DEVICE_STATUS_UnInitialized)
+ iwmx_cm_dev_disabled(wmxsdk);
+ break;
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ iwmx_cm_dev_connected(wmxsdk);
+ break;
+ default:
+ connman_error("wmxsdk: unknown new status %d\n", old_status);
+ return;
+ };
+ wmxsdk->status = __new_status;
+}
+
+/*
+ * Implement a device state transition [locking version]
+ *
+ * See __iwmx_cm_state_change()
+ */
+void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+ WIMAX_API_DEVICE_STATUS __new_status)
+{
+ g_mutex_lock(wmxsdk->status_mutex);
+ __iwmx_cm_state_change(wmxsdk, __new_status);
+ g_mutex_unlock(wmxsdk->status_mutex);
+}
+
+/*
+ * Read the cached device status
+ */
+WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk)
+{
+ WIMAX_API_DEVICE_STATUS status;
+
+ g_mutex_lock(wmxsdk->status_mutex);
+ status = wmxsdk->status;
+ g_mutex_unlock(wmxsdk->status_mutex);
+ return status;
+}
+
+/*
+ * Called by connman when a device is enabled by the user
+ *
+ * We need to turn the radio on; the state change function will poke
+ * the internals.
+ */
+static int iwmx_cm_enable(struct connman_device *dev)
+{
+ int result;
+ struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+
+ connman_inet_ifup(connman_device_get_index(dev));
+ result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_ON);
+ return result;
+}
+
+/*
+ * Called by connman when a device is disabled by the user
+ *
+ * Simple: just make sure the radio is off; the state change function
+ * will poke the internals.
+ */
+static int iwmx_cm_disable(struct connman_device *dev)
+{
+ int result;
+ struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+
+ result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_OFF);
+ connman_inet_ifdown(connman_device_get_index(dev));
+ return 0;
+}
+
+/*
+ * Probe deferred call from when the mainloop is idle
+ *
+ * probe() schedules this to be called from the mainloop when idle to
+ * do a device status evaluation. Needed because of an internal race
+ * condition in connman. FIXME: deploy into _probe() when fixed.
+ */
+static gboolean __iwmx_cm_probe_dpc(gpointer _wmxsdk)
+{
+ int result;
+ struct wmxsdk *wmxsdk = _wmxsdk;
+ result = iwmx_sdk_get_device_status(wmxsdk);
+ if (result < 0)
+ connman_error("wmxsdk: can't get status: %d\n", result);
+ else
+ iwmx_cm_state_change(wmxsdk, result);
+ return FALSE;
+}
+
+/*
+ * Called by connman when a new device pops in
+ *
+ * We allocate our private structure, register with the WiMAX API,
+ * open their device, subscribe to all the callbacks.
+ *
+ * At the end, we launch a deferred call (to work around current
+ * connman issues that need to be fixed in the future) and update the
+ * device's status. This allows us to pick up the current status and
+ * adapt connman's idea of the device to it.
+ */
+static int iwmx_cm_probe(struct connman_device *dev)
+{
+ int result;
+ struct wmxsdk *wmxsdk = NULL;
+
+ wmxsdk = connman_device_get_data(dev);
+ if (wmxsdk == NULL)
+ /* not called from a discovery done by the WiMAX
+ * Network Service, ignore */
+ return -ENODEV;
+
+ result = iwmx_sdk_setup(wmxsdk);
+ if (result < 0)
+ goto error_setup;
+
+ /* There is a race condition in the connman core that doesn't
+ * allow us to call this directly and things to work properly
+ * FIXME FIXME FIXME: merge _dpc call in here when connman is fixed */
+ g_idle_add(__iwmx_cm_probe_dpc, wmxsdk);
+ return 0;
+
+ iwmx_sdk_remove(wmxsdk);
+error_setup:
+ return result;
+}
+
+/*
+ * Called when a device is removed from connman
+ *
+ * Cleanup all that is done in _probe. Remove callbacks, unregister
+ * from the WiMAX API.
+ */
+static void iwmx_cm_remove(struct connman_device *dev)
+{
+ struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+ iwmx_sdk_remove(wmxsdk);
+}
+
+/*
+ * Called by connman to ask the device to scan for networks
+ *
+ * We have set in the WiMAX API the scan result callbacks, so we just
+ * start a simple scan (not a wide one).
+ *
+ * First we obtain the current list of networks and pass it to the
+ * callback processor. Then we start an scan cycle.
+ */
+static int iwmx_cm_scan(struct connman_device *dev)
+{
+ struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+ return iwmx_sdk_scan(wmxsdk);
+}
+
+/*
+ * Driver for a WiMAX API based device.
+ */
+static struct connman_device_driver iwmx_cm_device_driver = {
+ .name = "iwmx",
+ .type = CONNMAN_DEVICE_TYPE_WIMAX,
+ .probe = iwmx_cm_probe,
+ .remove = iwmx_cm_remove,
+ .enable = iwmx_cm_enable,
+ .disable = iwmx_cm_disable,
+ .scan = iwmx_cm_scan,
+};
+
+static int iwmx_cm_init(void)
+{
+ int result;
+
+ result = connman_device_driver_register(&iwmx_cm_device_driver);
+ if (result < 0)
+ goto error_driver_register;
+ result = connman_network_driver_register(&iwmx_cm_network_driver);
+ if (result < 0)
+ goto error_network_driver_register;
+ result = iwmx_sdk_api_init();
+ if (result < 0)
+ goto error_iwmx_sdk_init;
+ return 0;
+
+error_iwmx_sdk_init:
+ connman_network_driver_unregister(&iwmx_cm_network_driver);
+error_network_driver_register:
+ connman_device_driver_unregister(&iwmx_cm_device_driver);
+error_driver_register:
+ return result;
+}
+
+static void iwmx_cm_exit(void)
+{
+ iwmx_sdk_api_exit();
+ connman_network_driver_unregister(&iwmx_cm_network_driver);
+ connman_device_driver_unregister(&iwmx_cm_device_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(iwmx, "Intel WiMAX SDK / Common API plugin",
+ CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_LOW,
+ iwmx_cm_init, iwmx_cm_exit);
diff --git a/plugins/iwmx.h b/plugins/iwmx.h
new file mode 100644
index 00000000..265c00cc
--- /dev/null
+++ b/plugins/iwmx.h
@@ -0,0 +1,171 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2007-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
+ *
+ */
+
+/*
+ *
+ * The plugin is broken in two main parts: the glue to connman
+ * (iwmx_cm_*() functions) and the glue to the libiWmxSdk (iwmx_sdk_*()
+ * functions). They connect using a well defined interface.
+ *
+ * The plugin is state based and operates reactively to state
+ * transtitions on the WiMAX device or to user requests, even from
+ * external control tools that are not aware of connman.
+ *
+ * When the user requests connman to do something, it goes into a call
+ * implemented by the 'struct connman_driver iwmx_cm_driver' (or
+ * iwmx_cm_network_driver) that will instruct libiWmxSDK to change the
+ * device's state.
+ *
+ * When the device changes state, a state change callback is sent back
+ * by libiWmxSDK, which gets fed to iwmx_cm_state_change(), which
+ * evaluates the state change and updates connman's internal state in
+ * response.
+ *
+ * This allows the device to be also controlled by external tools
+ * without driving connman out of state.
+ *
+ * Device's state changes can be caused through:
+ *
+ * - connman (by user request)
+ *
+ * - any other external utility (eg: WmxSDK's wimaxcu)
+ *
+ * - external stimuli: network connection broken when going out of
+ * range
+ *
+ * Functions named __*() normally indicate that require locking. See
+ * their doc header.
+ *
+ * ENUMERATION
+ *
+ * When we receive a normal probe request [iwmx_cm_probe()] from
+ * connman, we ignore it (we can tell based on the connman device
+ * having NULL data).
+ *
+ * The plugin has registered with the WiMAX Network Service and it
+ * will listen to its device add/rm messages [iwmx_sdk_addremove_cb()]
+ * and use that to create a device [iwmx_sdk_dev_add()] which will be
+ * registered with connman. [iwmx_cm_dev_add()]. Then connman will
+ * enumerate the device, call again iwmx_cm_probe() and at this time,
+ * we'll recognize it, pass through iwmx_sdk_setup() and complete the
+ * probe process.
+ *
+ * If the daemon dies, in theory the plugin will realize and remove
+ * the WiMAX device.
+ */
+
+struct wmxsdk {
+ struct WIMAX_API_DEVICE_ID device_id;
+ struct connman_device *dev;
+
+ GStaticMutex network_mutex;
+
+ WIMAX_API_DEVICE_STATUS status;
+ GMutex *status_mutex;
+
+ /*
+ * nw points to the network we are connected to. connecting_nw
+ * points to the network we have requested to connect.
+ */
+ GMutex *connect_mutex;
+ struct connman_network *connecting_nw, *nw;
+
+ char name[100];
+ char ifname[16];
+};
+
+/* Initialize a [zeroed] struct wmxsdk */
+static inline void wmxsdk_init(struct wmxsdk *wmxsdk)
+{
+ g_static_mutex_init(&wmxsdk->network_mutex);
+
+ wmxsdk->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
+ wmxsdk->status_mutex = g_mutex_new();
+ g_assert(wmxsdk->status_mutex);
+
+ wmxsdk->connect_mutex = g_mutex_new();
+ g_assert(wmxsdk->connect_mutex);
+}
+
+/* Misc utilities */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define container_of(pointer, type, member) \
+({ \
+ type *object = NULL; \
+ size_t offset = (void *) &object->member - (void *) object; \
+ (type *) ((void *) pointer - offset); \
+})
+
+/* Misc values */
+enum {
+ /**
+ * Time we wait for callbacks: 5s
+ *
+ * I know, it is huge, but L4 and the device sometimes take
+ * some time, especially when there is crypto involved.
+ */
+ IWMX_SDK_L4_TIMEOUT_US = 5 * 1000 * 1000,
+
+ /*
+ * WARNING!!!!!
+ *
+ * ONLY ONE DEVICE SUPPORTED
+ *
+ * - on removal, there is no way to know which device was
+ * removed (the removed device is removed from the list and
+ * the callback doesn't have any more information than the
+ * index in the list that getlistdevice would return -- racy
+ * as hell).
+ *
+ * - on insertion, there is not enough information provided.
+ */
+ IWMX_SDK_DEV_MAX = 1,
+};
+
+struct connman_network *__iwmx_cm_network_available(
+ struct wmxsdk *wmxsdk, const char *station_name,
+ const char *station_type,
+ const void *sdk_nspname, size_t sdk_nspname_size,
+ int strength);
+
+struct connman_network *iwmx_cm_network_available(
+ struct wmxsdk *wmxsdk, const char *station_name,
+ const char *station_type,
+ const void *sdk_nspname, size_t sdk_nspname_size,
+ int strength);
+
+WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk);
+void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+ WIMAX_API_DEVICE_STATUS __new_status);
+void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+ WIMAX_API_DEVICE_STATUS __new_status);
+
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw);
+int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk);
+struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk);
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status);
+int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state);
+WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk);
+int iwmx_sdk_setup(struct wmxsdk *wmxsdk);
+void iwmx_sdk_remove(struct wmxsdk *wmxsdk);
+int iwmx_sdk_scan(struct wmxsdk *wmxsdk);
+int iwmx_sdk_api_init(void);
+void iwmx_sdk_api_exit(void);
diff --git a/plugins/iwmxsdk.c b/plugins/iwmxsdk.c
index c30004f7..60466e5c 100644
--- a/plugins/iwmxsdk.c
+++ b/plugins/iwmxsdk.c
@@ -23,55 +23,993 @@
#include <config.h>
#endif
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <net/if.h>
+
+#include <glib.h>
+
#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
#include <connman/device.h>
+#include <connman/inet.h>
#include <connman/log.h>
-static int iwmxsdk_probe(struct connman_device *device)
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "iwmx.h"
+
+/* Yes, this is dirty; see above on IWMX_SDK_DEV_MAX*/
+static struct wmxsdk g_iwmx_sdk_devs[IWMX_SDK_DEV_MAX];
+
+static struct wmxsdk *deviceid_to_wmxsdk(struct WIMAX_API_DEVICE_ID *device_id)
{
- DBG("device %p", device);
+ return container_of(device_id, struct wmxsdk, device_id);
+}
- return 0;
+static struct WIMAX_API_DEVICE_ID g_api;
+
+
+/*
+ * FIXME: pulled it it out of some hole
+ *
+ * the cinr to percentage computation comes from the L3/L4 doc
+ *
+ * But some other places (L4 code) have a more complex, seemingly
+ * logarithmical computation.
+ *
+ * Oh well...
+ *
+ */
+static int cinr_to_percentage(int cinr)
+{
+ int strength;
+ if (cinr <= -5)
+ strength = 0;
+ else if (cinr >= 25)
+ strength = 100;
+ else /* Calc percentage on the value from -5 to 25 */
+ strength = ((100UL * (cinr - -5)) / (25 - -5));
+ return strength;
}
-static void iwmxsdk_remove(struct connman_device *device)
+/*
+ * Convert a WiMAX API status to an string.
+ */
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
{
- DBG("device %p", device);
+ switch (status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ return "Uninitialized";
+ break;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ return "Device RF Off(both H/W and S/W)";
+ break;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ return "Device RF Off(via H/W switch)";
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ return "Device RF Off(via S/W switch)";
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ return "Device is ready";
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ return "Device is scanning";
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ return "Connection in progress";
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ return "Layer 2 connected";
+ case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+ return "Idle connection";
+ default:
+ return "unknown state";
+ }
}
-static int iwmxsdk_enable(struct connman_device *device)
+/*
+ * Get the device's status from the device
+ *
+ * Does NOT cache the result
+ * Does NOT trigger a state change in connman
+ *
+ * Returns < 0 errno code on error, status code if ok.
+ */
+WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
{
- DBG("device %p", device);
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
- return 0;
+ WIMAX_API_DEVICE_STATUS dev_status;
+ WIMAX_API_CONNECTION_PROGRESS_INFO pi;
+
+ r = GetDeviceStatus(&wmxsdk->device_id, &dev_status, &pi);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot read device state: %d (%s)\n",
+ r, errstr);
+ dev_status = -EIO;
+ }
+ return dev_status;
}
-static int iwmxsdk_disable(struct connman_device *device)
+/*
+ * Get the device's status from the device but return a string describing it
+ *
+ * Same conditions as iwmx_sdk_get_device_status().
+ */
+static const char *iwmx_sdk_get_device_status_str(struct wmxsdk *wmxsdk)
{
- DBG("device %p", device);
+ const char *result;
+ WIMAX_API_DEVICE_STATUS dev_status;
- return 0;
+ dev_status = iwmx_sdk_get_device_status(wmxsdk);
+ if ((int) dev_status < 0)
+ result = "cannot read device state";
+ else
+ result = iwmx_sdk_dev_status_to_str(dev_status);
+ return result;
}
-static struct connman_device_driver iwmxsdk_driver = {
- .name = "iwmxsdk",
- .type = CONNMAN_DEVICE_TYPE_WIMAX,
- .probe = iwmxsdk_probe,
- .remove = iwmxsdk_remove,
- .enable = iwmxsdk_enable,
- .disable = iwmxsdk_disable,
-};
+/*
+ * Translate a WiMAX network type to a readable name.
+ */
+static const char *iwmx_sdk_network_type_name(enum _WIMAX_API_NETWORK_TYPE network_type)
+{
+ static char *network_type_name[] = {
+ [WIMAX_API_HOME] = "",
+ [WIMAX_API_PARTNER] = " (partner network)",
+ [WIMAX_API_ROAMING_PARTNER] = " (roaming partner network)",
+ [WIMAX_API_UNKNOWN] = " (unknown network)",
+ };
+ if (network_type > WIMAX_API_UNKNOWN)
+ return "(BUG! UNKNOWN NETWORK_TYPE MODE)";
+ else
+ return network_type_name[network_type];
+}
-static int iwmxsdk_init(void)
+/*
+ * If the device is connected but we don't know about the network,
+ * create the knowledge of it.
+ *
+ * Asks the WiMAX API to report which NSP we are connected to and we
+ * create/update a network_el in the device's network list. Then
+ * return it.
+ *
+ * Returns NULL on error.
+ *
+ * NOTE: wmxsdk->network_mutex has to be taken
+ */
+struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
{
- return connman_device_driver_register(&iwmxsdk_driver);
+ struct connman_network *nw;
+
+ struct WIMAX_API_CONNECTED_NSP_INFO nsp_info;
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ /* The device is getting connected due to an external (to
+ * connman) event; find which is the nw we are getting
+ * connected to. if we don't have it, add it */
+ r = GetConnectedNSP(&wmxsdk->device_id, &nsp_info);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error(
+ "wmxsdk: Cannot get connected NSP info: %d (%s)\n",
+ r, errstr);
+ strcpy((char *) nsp_info.NSPName, "unknown");
+ nw = iwmx_cm_network_available(
+ wmxsdk, "unknown",
+ iwmx_sdk_network_type_name(WIMAX_API_UNKNOWN),
+ nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
+ cinr_to_percentage(nsp_info.CINR - 10));
+ } else {
+ nw = iwmx_cm_network_available(
+ wmxsdk, (char *) nsp_info.NSPName,
+ iwmx_sdk_network_type_name(nsp_info.networkType),
+ nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
+ cinr_to_percentage(nsp_info.CINR - 10));
+ }
+ return nw;
}
-static void iwmxsdk_exit(void)
+/*
+ * Callback for a RF State command
+ *
+ * Called by the WiMAX API when a command sent to change the RF state
+ * is completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * We don't do anything, as when the device changes state, the state
+ * change callback is called and that will fiddle with the connman
+ * internals.
+ */
+static void __iwmx_sdk_rf_state_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ WIMAX_API_RF_STATE rf_state)
{
- connman_device_driver_unregister(&iwmxsdk_driver);
+ DBG("rf_state changed to %d\n", rf_state);
}
-CONNMAN_PLUGIN_DEFINE(iwmxsdk, "Intel WiMAX interface plugin", VERSION,
- CONNMAN_PLUGIN_PRIORITY_DEFAULT, iwmxsdk_init, iwmxsdk_exit)
+/*
+ * Turn the radio on or off
+ *
+ * First it checks that we are in the right state before doing
+ * anything; there might be no need to do anything.
+ *
+ * Issue a command to the WiMAX API, wait for a callback confirming it
+ * is done. Sometimes the callback is missed -- in that case, do force
+ * a state change evaluation.
+ *
+ * Frustration note:
+ *
+ * Geezoos efing Xist, they make difficult even the most simple
+ * of the operations
+ *
+ * This thing is definitely a pain. If the radio is ON already
+ * and you switch it on again...well, there is no way to tell
+ * because you don't get a callback saying it basically
+ * suceeded. But on the other hand, if the thing was in a
+ * different state and action needs to be taken, you have to wait
+ * for a callback to confirm it's done. However, there is also an
+ * state change callback, which is almost the same, so now you
+ * have to handle things in two "unrelated" threads of execution.
+ *
+ * How the shpx are you expected to tell the difference? Check
+ * status first? On timeout? Nice gap (eighteen wheeler size) for
+ * race conditions.
+ */
+int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state)
+{
+ int result;
+
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+ WIMAX_API_DEVICE_STATUS dev_status;
+
+ g_assert(rf_state == WIMAX_API_RF_ON || rf_state == WIMAX_API_RF_OFF);
+
+ /* Guess what the current radio state is; if it is ON
+ * already, don't redo it. */
+ dev_status = iwmx_sdk_get_device_status(wmxsdk);
+ if ((int) dev_status < 0) {
+ result = dev_status;
+ goto error_get_status;
+ }
+ switch (dev_status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ result = -EINVAL;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ connman_error(
+ "wmxsdk: cannot turn on radio: hw switch is off\n");
+ result = -EPERM;
+ goto error_cant_do;
+ break;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ if (rf_state == WIMAX_API_RF_OFF) {
+ result = 0;
+ DBG("radio is already off\n");
+ goto out_done;
+ }
+ break;
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+ if (rf_state == WIMAX_API_RF_ON) {
+ result = 0;
+ DBG("radio is already on\n");
+ goto out_done;
+ }
+ break;
+ default:
+ g_assert(1);
+ }
+ /* Ok, flip the radio */
+ r = CmdControlPowerManagement(&wmxsdk->device_id, rf_state);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot flip radio to %d: %d (%s) "
+ "[device is in state %s]\n",
+ rf_state, r, errstr,
+ iwmx_sdk_get_device_status_str(wmxsdk));
+ result = -EIO;
+ } else
+ result = -EINPROGRESS;
+out_done:
+error_cant_do:
+error_get_status:
+ return result;
+}
+
+/*
+ * Callback for a Connect command
+ *
+ * Called by the WiMAX API when a command sent to connect is
+ * completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * WE DON'T DO MUCH HERE -- the real meat happens when a state change
+ * callback is sent, where we detect we move to connected state (or
+ * from disconnecting to something else); the state change callback is
+ * called and that will fiddle with the connman internals.
+ */
+static void __iwmx_sdk_connect_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+ WIMAX_API_DEVICE_STATUS status;
+ struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+
+ status = iwmx_cm_status_get(wmxsdk);
+ if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+ if (status != WIMAX_API_DEVICE_STATUS_Data_Connected
+ && status != WIMAX_API_DEVICE_STATUS_Connection_Idle)
+ connman_error("wmxsdk: error: connect worked, but state"
+ " didn't change (now it is %d [%s])\n",
+ status,
+ iwmx_sdk_dev_status_to_str(status));
+ } else
+ connman_error("wmxsdk: failed to connect (status %d: %s)\n",
+ status, iwmx_sdk_dev_status_to_str(status));
+}
+
+/*
+ * Connect to a network
+ *
+ * This function starts the connection process to a given network;
+ * when the device changes status, the status change callback will
+ * tell connman if the network is finally connected or not.
+ *
+ * One of the reasons it is done like that is to allow external tools
+ * to control the device and the plugin just passing the status so
+ * connman displays the right info.
+ */
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw)
+{
+ int result;
+
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+ WIMAX_API_DEVICE_STATUS dev_status;
+ const char *station_name = connman_network_get_identifier(nw);
+ const void *sdk_nspname;
+ unsigned int sdk_nspname_size;
+
+ g_mutex_lock(wmxsdk->connect_mutex);
+ /* Guess what the current radio state is; if it is ON
+ * already, don't redo it. */
+ dev_status = iwmx_cm_status_get(wmxsdk);
+ if ((int) dev_status < 0) {
+ result = dev_status;
+ goto error_get_status;
+ }
+ switch (dev_status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
+ result = -EINVAL;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ connman_error("wmxsdk: Cannot connect: radio is off\n");
+ result = -EPERM;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ break;
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ DBG("Connect already pending, waiting for it\n");
+ result = -EINPROGRESS;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+ connman_error("wmxsdk: BUG? need to disconnect?\n");
+ result = -EINVAL;
+ goto error_cant_do;
+ default:
+ g_assert(1);
+ }
+
+ /* Ok, do the connection, wait for a callback */
+ wmxsdk->connecting_nw = connman_network_ref(nw);
+ sdk_nspname = connman_network_get_blob(nw, "WiMAX.NSP.name",
+ &sdk_nspname_size);
+ g_assert(sdk_nspname != NULL);
+ r = CmdConnectToNetwork(&wmxsdk->device_id, (void *) sdk_nspname, 0, 0);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot connect to network %s: %d (%s)"
+ " - device is in state '%s'\n",
+ station_name, r, errstr,
+ iwmx_sdk_get_device_status_str(wmxsdk));
+ result = -EIO;
+ connman_network_unref(nw);
+ wmxsdk->connecting_nw = NULL;
+ } else
+ result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+ g_mutex_unlock(wmxsdk->connect_mutex);
+ return result;
+}
+
+/*
+ * Callback for a Disconnect command
+ *
+ * Called by the WiMAX API when a command sent to connect is
+ * completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * When the device changes state, the state change callback is called
+ * and that will fiddle with the connman internals.
+ *
+ * We just update the result of the command and wake up anybody who is
+ * waiting for this conditional variable.
+ */
+static void __iwmx_sdk_disconnect_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+ struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+ WIMAX_API_DEVICE_STATUS status;
+
+ status = iwmx_cm_status_get(wmxsdk);
+ if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+ if (status == WIMAX_API_DEVICE_STATUS_Data_Connected
+ || status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+ connman_error("wmxsdk: error: disconnect worked, "
+ "but state didn't change (now it is "
+ "%d [%s])\n", status,
+ iwmx_sdk_dev_status_to_str(status));
+ } else
+ connman_error("wmxsdk: failed to disconnect (status %d: %s)\n",
+ status, iwmx_sdk_dev_status_to_str(status));
+}
+
+/*
+ * Disconnect from a network
+ *
+ * This function tells the device to disconnect; the state change
+ * callback will take care of inform connman's internals.
+ */
+int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk)
+{
+ int result;
+
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+ WIMAX_API_DEVICE_STATUS dev_status;
+
+ g_mutex_lock(wmxsdk->connect_mutex);
+ /* Guess what the current radio state is; if it is ON
+ * already, don't redo it. */
+ dev_status = iwmx_sdk_get_device_status(wmxsdk);
+ if ((int) dev_status < 0) {
+ result = dev_status;
+ goto error_get_status;
+ }
+ switch (dev_status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
+ result = -EINVAL;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ DBG("Cannot disconnect, radio is off; ignoring\n");
+ result = 0;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ DBG("Cannot disconnect, already disconnected; ignoring\n");
+ result = 0;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+ break;
+ default:
+ g_assert(1);
+ }
+ /* Ok, flip the radio */
+ r = CmdDisconnectFromNetwork(&wmxsdk->device_id);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot disconnect from network: "
+ "%d (%s)\n", r, errstr);
+ result = -EIO;
+ } else
+ result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+ g_mutex_unlock(wmxsdk->connect_mutex);
+ return result;
+}
+
+/*
+ * Callback for state change messages
+ *
+ * Just pass them to the state transition handler
+ */
+static void __iwmx_sdk_state_change_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ WIMAX_API_DEVICE_STATUS status,
+ WIMAX_API_STATUS_REASON reason,
+ WIMAX_API_CONNECTION_PROGRESS_INFO pi)
+{
+ struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+ iwmx_cm_state_change(wmxsdk, status);
+}
+
+/*
+ * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
+ * are available.
+ *
+ * From here we update the connman core idea of which networks are
+ * available.
+ */
+static void __iwmx_sdk_scan_common_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ struct WIMAX_API_NSP_INFO_EX *nsp_list,
+ UINT32 nsp_list_size)
+{
+ struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+ unsigned itr;
+ char station_name[256];
+
+ g_static_mutex_lock(&wmxsdk->network_mutex);
+ for (itr = 0; itr < nsp_list_size; itr++) {
+ int strength;
+ struct WIMAX_API_NSP_INFO_EX *nsp_info = &nsp_list[itr];
+ snprintf(station_name, sizeof(station_name),
+ "%s", (char *)nsp_info->NSPName);
+ /* CAPI is reporing link quality as zero -- if it is
+ * zero, check if it is a bug by computing it based on
+ * CINR. If it is different, use the computed one. */
+ strength = nsp_info->linkQuality;
+ if (strength == 0) { /* huh */
+ int linkq_expected =
+ cinr_to_percentage(nsp_info->CINR - 10);
+ if (linkq_expected != strength)
+ strength = linkq_expected;
+ }
+
+ __iwmx_cm_network_available(
+ wmxsdk, station_name,
+ iwmx_sdk_network_type_name(nsp_info->networkType),
+ nsp_info->NSPName,
+ strlen((char *) nsp_info->NSPName) + 1,
+ strength);
+ }
+ g_static_mutex_unlock(&wmxsdk->network_mutex);
+}
+
+/*
+ * Called by the WiMAX API when we get a wide scan result
+ *
+ * We treat them same as wide, so we just call that.
+ */
+static void __iwmx_sdk_wide_scan_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ struct WIMAX_API_NSP_INFO_EX *nsp_list,
+ UINT32 nsp_list_size)
+{
+ __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
+}
+
+/*
+ * Called by the WiMAX API when we get a normal (non wide) scan result
+ *
+ * We treat them same as wide, so we just call that.
+ */
+static void __iwmx_sdk_scan_cb(struct WIMAX_API_DEVICE_ID *device_id,
+ struct WIMAX_API_NSP_INFO_EX *nsp_list,
+ UINT32 nsp_list_size, UINT32 searchProgress)
+{
+ __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
+}
+
+/*
+ * Called to ask the device to scan for networks
+ *
+ * We don't really scan as the WiMAX SDK daemon scans in the
+ * background for us. We just get the results. See iwmx_sdk_setup().
+ */
+int iwmx_sdk_scan(struct wmxsdk *wmxsdk)
+{
+ int result;
+
+ UINT32 nsp_list_length = 10;
+ struct WIMAX_API_NSP_INFO_EX nsp_list[10]; /* FIXME: up to 32? */
+
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ r = GetNetworkListEx(&wmxsdk->device_id, nsp_list, &nsp_list_length);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot get network list: %d (%s)\n",
+ r, errstr);
+ result = -EIO;
+ goto error_scan;
+ }
+
+ if (nsp_list_length == 0)
+ DBG("no networks\n");
+ else
+ __iwmx_sdk_scan_common_cb(&wmxsdk->device_id, nsp_list,
+ nsp_list_length);
+ result = 0;
+error_scan:
+ return result;
+}
+
+/*
+ * Initialize the WiMAX API, register with it, setup callbacks
+ *
+ * Called through
+ *
+ * iwmx_sdk_dev_add
+ * connman_inet_create_device
+ * connman_register
+ * iwmx_cm_probe()
+ */
+int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
+{
+ int result;
+
+ WIMAX_API_RET r;
+
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ result = -ENFILE;
+
+ /* device_id initialized by iwmx_sdk_dev_add */
+
+ r = WiMaxDeviceOpen(&wmxsdk->device_id);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot open device: %d (%s)\n",
+ r, errstr);
+ goto error_wimaxdeviceopen;
+ }
+
+ /*
+ * We scan in auto mode (in the background)
+ *
+ * Otherwise is messy -- if we have connman triggering a scan
+ * when we call iwmx_cm_scan() -> iwmx_sdk_scan(), most of the
+ * times that causes a race condition when the UI asks for a
+ * scan right before displaying the network menu. As there is
+ * no way to cancel an ongoing scan before connecting, we are
+ * stuck. So we do auto bg and have iwmx_sdk_scan() just return
+ * the current network list.
+ */
+ r = SetConnectionMode(&wmxsdk->device_id,
+ WIMAX_API_CONNECTION_AUTO_SCAN_MANUAL_CONNECT);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot set connectin mode to manual: "
+ "%d (%s)\n", r, errstr);
+ goto error_connection_mode;
+ }
+
+ r = SubscribeControlPowerManagement(&wmxsdk->device_id,
+ __iwmx_sdk_rf_state_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot subscribe to radio change "
+ "events: %u (%s)\n", r, errstr);
+ result = -EIO;
+ goto error_subscribe_rf_state;
+ }
+
+ r = SubscribeDeviceStatusChange(&wmxsdk->device_id,
+ __iwmx_sdk_state_change_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot subscribe to state chaneg events:"
+ "%d (%s)\n", r, errstr);
+ goto error_subscribe_state_change;
+ }
+
+ r = SubscribeNetworkSearchWideScanEx(&wmxsdk->device_id,
+ __iwmx_sdk_wide_scan_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot subscribe to wide scan events: "
+ "%d (%s)\n", r, errstr);
+ goto error_subscribe_wide_scan;
+ }
+ r = SubscribeNetworkSearchEx(&wmxsdk->device_id, __iwmx_sdk_scan_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot subscribe to scan events: "
+ "%d (%s)\n", r, errstr);
+ goto error_subscribe_scan;
+ }
+
+ r = SubscribeConnectToNetwork(&wmxsdk->device_id,
+ __iwmx_sdk_connect_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot subscribe to connect events: "
+ "%d (%s)\n", r, errstr);
+ goto error_subscribe_connect;
+ }
+
+ r = SubscribeDisconnectToNetwork(&wmxsdk->device_id,
+ __iwmx_sdk_disconnect_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot subscribe to disconnect events: "
+ "%d (%s)\n", r, errstr);
+ goto error_subscribe_disconnect;
+ }
+ result = 0;
+out:
+ return result;
+
+ UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
+error_subscribe_disconnect:
+ UnsubscribeConnectToNetwork(&wmxsdk->device_id);
+error_subscribe_connect:
+ UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
+error_subscribe_scan:
+ UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
+error_subscribe_wide_scan:
+ UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
+error_subscribe_state_change:
+ UnsubscribeControlPowerManagement(&wmxsdk->device_id);
+error_subscribe_rf_state:
+error_connection_mode:
+ WiMaxDeviceClose(&wmxsdk->device_id);
+error_wimaxdeviceopen:
+ goto out;
+}
+
+/*
+ * Called when a device is removed from connman
+ *
+ * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
+ * unregister from the WiMAX API.
+ */
+void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
+{
+ UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
+ UnsubscribeConnectToNetwork(&wmxsdk->device_id);
+ UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
+ UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
+ UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
+ UnsubscribeControlPowerManagement(&wmxsdk->device_id);
+ WiMaxDeviceClose(&wmxsdk->device_id);
+}
+
+static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
+{
+ int result, ifindex;
+ struct wmxsdk *wmxsdk;
+ const char *s;
+
+ if (idx >= IWMX_SDK_DEV_MAX) {
+ connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
+ idx, IWMX_SDK_DEV_MAX);
+ goto error_bug;
+ }
+ wmxsdk = &g_iwmx_sdk_devs[idx];
+ if (wmxsdk->dev != NULL) {
+ connman_error("BUG! device index %u already enumerated?\n",
+ idx);
+ goto error_bug;
+ }
+
+ memset(wmxsdk, 0, sizeof(*wmxsdk));
+ wmxsdk_init(wmxsdk);
+ /*
+ * This depends on a hack in the WiMAX Network Service; it has
+ * to return, as part of the device name, a string "if:IFNAME"
+ * where the OS's device name is stored.
+ */
+ s = strstr(name, "if:");
+ if (s == NULL
+ || sscanf(s, "if:%15[^ \f\n\r\t\v]", wmxsdk->ifname) != 1) {
+ connman_error("Cannot extract network interface name off '%s'",
+ name);
+ goto error_noifname;
+ }
+ DBG("network interface name: '%s'", wmxsdk->ifname);
+
+ ifindex = if_nametoindex(wmxsdk->ifname);
+ if (ifindex <= 0) {
+ result = -ENFILE;
+ connman_error("wxmsdk: %s: cannot find interface index\n",
+ wmxsdk->ifname);
+ goto error_noifname;
+ }
+
+ wmxsdk->dev = connman_inet_create_device(ifindex);
+ if (wmxsdk->dev == NULL) {
+ connman_error("wmxsdk: %s: failed to create connman_device\n",
+ name);
+ goto error_create;
+ }
+ strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
+ connman_device_set_data(wmxsdk->dev, wmxsdk);
+
+ wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+ wmxsdk->device_id.deviceIndex = api_idx;
+
+ result = connman_device_register(wmxsdk->dev);
+ if (result < 0) {
+ connman_error("wmxsdk: %s: failed to register: %d\n",
+ wmxsdk->ifname, result);
+ goto error_dev_add;
+ }
+ return;
+
+error_dev_add:
+ wmxsdk->name[0] = 0;
+ connman_device_unref(wmxsdk->dev);
+ wmxsdk->dev = NULL;
+error_noifname:
+error_create:
+error_bug:
+ return;
+}
+
+static void iwmx_sdk_dev_rm(unsigned idx)
+{
+ struct wmxsdk *wmxsdk;
+
+ if (idx >= IWMX_SDK_DEV_MAX) {
+ connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
+ idx, IWMX_SDK_DEV_MAX);
+ goto error_bug;
+ }
+ wmxsdk = &g_iwmx_sdk_devs[idx];
+ if (wmxsdk->dev == NULL) {
+ DBG("device index %u not enumerated? ignoring\n", idx);
+ goto error_bug;
+ }
+
+ connman_device_unregister(wmxsdk->dev);
+ wmxsdk->name[0] = 0;
+ connman_device_unref(wmxsdk->dev);
+ memset(wmxsdk, 0, sizeof(*wmxsdk));
+error_bug:
+ return;
+}
+
+static void iwmx_sdk_addremove_cb(struct WIMAX_API_DEVICE_ID *devid,
+ BOOL presence)
+{
+ unsigned int cnt;
+ WIMAX_API_RET r;
+ struct WIMAX_API_HW_DEVICE_ID device_id_list[5];
+ UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
+
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ DBG("cb: handle %u index #%u is %d\n", devid->sdkHandle,
+ devid->deviceIndex, presence);
+
+ r = GetListDevice(devid, device_id_list, &device_id_list_size);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(devid, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot obtain list "
+ "of devices: %d (%s)\n", r, errstr);
+ return;
+ }
+
+ if (device_id_list_size == 0)
+ DBG("No WiMAX devices reported\n");
+ else
+ for (cnt = 0; cnt < device_id_list_size; cnt++) {
+ struct WIMAX_API_HW_DEVICE_ID *dev =
+ device_id_list + cnt;
+ DBG("#%u index #%u device %s\n",
+ cnt, dev->deviceIndex, dev->deviceName);
+ }
+ if (device_id_list_size < devid->deviceIndex) {
+ connman_error("wmxsdk: changed device (%u) not in the list? "
+ "(%u items)\n",
+ devid->deviceIndex, device_id_list_size);
+ return;
+ }
+
+ if (presence) {
+ struct WIMAX_API_HW_DEVICE_ID *dev =
+ device_id_list + devid->deviceIndex;
+ iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex,
+ dev->deviceName);
+ } else {
+ iwmx_sdk_dev_rm(devid->deviceIndex);
+ }
+}
+
+/*
+ * Initialize the WiMAX API, register with it, setup callbacks for
+ * device coming up / dissapearing
+ */
+int iwmx_sdk_api_init(void)
+{
+ int result;
+ unsigned int cnt;
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ struct WIMAX_API_HW_DEVICE_ID device_id_list[5];
+ UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
+
+ memset(&g_api, 0, sizeof(g_api));
+ g_api.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+
+ result = -EIO;
+ r = WiMaxAPIOpen(&g_api);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&g_api, r, errstr, &errstr_size);
+ connman_error("wmxsdk: WiMaxAPIOpen failed with %d (%s)\n",
+ r, errstr);
+ goto error_wimaxapiopen;
+ }
+
+ r = SubscribeDeviceInsertRemove(&g_api, iwmx_sdk_addremove_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&g_api, r, errstr, &errstr_size);
+ connman_error("wmxsdk: insert/remove subscribe failed with "
+ "%d (%s)\n", r, errstr);
+ goto error_close;
+ }
+
+ r = GetListDevice(&g_api, device_id_list, &device_id_list_size);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&g_api, r, errstr, &errstr_size);
+ connman_error("wmxsdk: Cannot obtain list "
+ "of devices: %d (%s)\n", r, errstr);
+ goto error_close;
+ }
+ if (device_id_list_size < g_api.deviceIndex) {
+ connman_error("wmxsdk: changed device (%u) not in the list? "
+ "(%u items)\n",
+ g_api.deviceIndex, device_id_list_size);
+ }
+
+ if (device_id_list_size == 0)
+ DBG("No WiMAX devices reported\n");
+ else
+ for (cnt = 0; cnt < device_id_list_size; cnt++) {
+ struct WIMAX_API_HW_DEVICE_ID *dev =
+ device_id_list + cnt;
+ DBG("#%u index #%u device %s\n",
+ cnt, dev->deviceIndex, dev->deviceName);
+ iwmx_sdk_dev_add(cnt, dev->deviceIndex,
+ dev->deviceName);
+ }
+ return 0;
+
+error_close:
+ WiMaxAPIClose(&g_api);
+error_wimaxapiopen:
+ return result;
+}
+
+void iwmx_sdk_api_exit(void)
+{
+ WIMAX_API_RET r;
+
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ r = WiMaxAPIClose(&g_api);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&g_api, r, errstr, &errstr_size);
+ connman_error("wmxsdk: WiMaxAPIClose failed with %d (%s)\n",
+ r, errstr);
+ }
+ return;
+}