Add plugin for Intel WiMAX SDK
[connman] / plugins / iwmxsdk.c
index c30004f..60466e5 100644 (file)
 #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;
+}