From 00d98e92bf5976a721caa68fc100fcc19068410b Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Sat, 23 May 2009 00:44:46 -0700 Subject: [PATCH] Add plugin for Intel WiMAX SDK --- bootstrap-configure | 2 +- configure.ac | 22 +- plugins/Makefile.am | 12 +- plugins/iwmx.c | 587 +++++++++++++++++++++++++++++++ plugins/iwmx.h | 171 +++++++++ plugins/iwmxsdk.c | 990 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 1743 insertions(+), 41 deletions(-) create mode 100644 plugins/iwmx.c create mode 100644 plugins/iwmx.h diff --git a/bootstrap-configure b/bootstrap-configure index ec0100a..6e5bfdc 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 a824605..c9a4336 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 6ffdde1..07535f7 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 0000000..ccbd1d2 --- /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 +#endif + +#include +#include +#include + +#include + +#define CONNMAN_API_SUBJECT_TO_CHANGE +#include +#include +#include +#include + +#include +#include + +#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 0000000..265c00c --- /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 c30004f..60466e5 100644 --- a/plugins/iwmxsdk.c +++ b/plugins/iwmxsdk.c @@ -23,55 +23,993 @@ #include #endif +#include +#include +#include +#include + +#include + #define CONNMAN_API_SUBJECT_TO_CHANGE -#include #include +#include #include -static int iwmxsdk_probe(struct connman_device *device) +#include +#include + +#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; +} -- 2.7.4