summaryrefslogtreecommitdiff
path: root/src/devices/wimax/iwmxsdk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wimax/iwmxsdk.c')
-rw-r--r--src/devices/wimax/iwmxsdk.c1517
1 files changed, 1517 insertions, 0 deletions
diff --git a/src/devices/wimax/iwmxsdk.c b/src/devices/wimax/iwmxsdk.c
new file mode 100644
index 000000000..d1ef7b683
--- /dev/null
+++ b/src/devices/wimax/iwmxsdk.c
@@ -0,0 +1,1517 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+
+#include <glib.h>
+
+#include <WiMaxType.h>
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "logging/nm-logging.h"
+#include "iwmxsdk.h"
+
+static WIMAX_API_DEVICE_ID g_api;
+static GMutex add_remove_mutex;
+
+/* Misc utilities */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/* Misc values */
+enum {
+ /*
+ * 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,
+};
+
+/* 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(WIMAX_API_DEVICE_ID *device_id)
+{
+ unsigned cnt;
+ for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
+ struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
+ if (wmxsdk &&
+ wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
+ return wmxsdk;
+ }
+ return NULL;
+}
+
+static int deviceid_to_index(WIMAX_API_DEVICE_ID *device_id)
+{
+ unsigned cnt;
+
+ for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
+ struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
+ if (wmxsdk && wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
+ return cnt;
+ }
+ return -1;
+}
+
+struct wmxsdk *iwmx_sdk_get_wmxsdk_for_iface(const char *iface)
+{
+ unsigned cnt;
+
+ for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
+ struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
+ if (wmxsdk && !strcmp(wmxsdk->ifname, iface))
+ return wmxsdk;
+ }
+ return NULL;
+}
+
+/*
+ * 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;
+}
+
+/**************************************************************/
+
+typedef struct {
+ WimaxNewWmxsdkFunc callback;
+ void *user_data;
+} NewSdkCallback;
+
+static GMutex new_callbacks_mutex;
+static GSList *new_callbacks = NULL;
+
+void iwmx_sdk_new_callback_register(WimaxNewWmxsdkFunc callback, void *user_data)
+{
+ NewSdkCallback *cb;
+
+ cb = g_malloc0 (sizeof (NewSdkCallback));
+ g_assert (cb);
+ cb->callback = callback;
+ cb->user_data = user_data;
+
+ g_mutex_lock (&new_callbacks_mutex);
+ new_callbacks = g_slist_append (new_callbacks, cb);
+ g_mutex_unlock (&new_callbacks_mutex);
+}
+
+void iwmx_sdk_new_callback_unregister(WimaxNewWmxsdkFunc callback, void *user_data)
+{
+ GSList *iter;
+ NewSdkCallback *found = NULL;
+
+ g_mutex_lock (&new_callbacks_mutex);
+ for (iter = new_callbacks; iter; iter = g_slist_next (iter)) {
+ NewSdkCallback *cb = iter->data;
+
+ if (cb->callback == callback && cb->user_data == user_data) {
+ found = cb;
+ break;
+ }
+ }
+
+ if (found) {
+ new_callbacks = g_slist_remove (new_callbacks, found);
+ g_free (found);
+ }
+ g_mutex_unlock (&new_callbacks_mutex);
+}
+
+static void iwmx_sdk_call_new_callbacks(struct wmxsdk *wmxsdk)
+{
+ GSList *iter;
+
+ g_mutex_lock (&new_callbacks_mutex);
+ for (iter = new_callbacks; iter; iter = g_slist_next (iter)) {
+ NewSdkCallback *cb = iter->data;
+
+ cb->callback (wmxsdk, cb->user_data);
+ }
+ g_mutex_unlock (&new_callbacks_mutex);
+}
+
+/****************************************************************/
+
+typedef struct {
+ struct wmxsdk *wmxsdk;
+ WIMAX_API_DEVICE_STATUS new_status;
+ WIMAX_API_DEVICE_STATUS old_status;
+ WIMAX_API_STATUS_REASON reason;
+ WIMAX_API_CONNECTION_PROGRESS_INFO progress;
+} StateChangeInfo;
+
+static gboolean
+state_change_handler(gpointer user_data)
+{
+ StateChangeInfo *info = user_data;
+
+ if (info->wmxsdk->state_change_cb) {
+ info->wmxsdk->state_change_cb(info->wmxsdk,
+ info->new_status,
+ info->old_status,
+ info->reason,
+ info->progress,
+ info->wmxsdk->callback_data);
+ }
+ wmxsdk_unref(info->wmxsdk);
+ memset(info, 0, sizeof(*info));
+ free(info);
+ return FALSE;
+}
+
+static void
+_schedule_state_change(struct wmxsdk *wmxsdk,
+ WIMAX_API_DEVICE_STATUS new_status,
+ WIMAX_API_DEVICE_STATUS old_status,
+ WIMAX_API_STATUS_REASON reason,
+ WIMAX_API_CONNECTION_PROGRESS_INFO progress)
+{
+ StateChangeInfo *info;
+
+ info = malloc(sizeof (*info));
+ if (!info)
+ return;
+
+ memset(info, 0, sizeof(*info));
+ info->wmxsdk = wmxsdk;
+ info->new_status = new_status;
+ info->old_status = old_status;
+ info->reason = reason;
+ info->progress = progress;
+
+ wmxsdk_ref(wmxsdk);
+ g_idle_add(state_change_handler, info);
+}
+
+typedef struct {
+ struct wmxsdk *wmxsdk;
+ WIMAX_API_MEDIA_STATUS media_status;
+} MediaStatusInfo;
+
+static gboolean
+media_status_change_handler(gpointer user_data)
+{
+ MediaStatusInfo *info = user_data;
+
+ if (info->wmxsdk->media_status_cb) {
+ info->wmxsdk->media_status_cb(info->wmxsdk,
+ info->media_status,
+ info->wmxsdk->callback_data);
+ }
+ wmxsdk_unref(info->wmxsdk);
+ memset(info, 0, sizeof(*info));
+ free(info);
+ return FALSE;
+}
+
+static void
+_schedule_media_status_change(struct wmxsdk *wmxsdk,
+ WIMAX_API_MEDIA_STATUS media_status)
+{
+ MediaStatusInfo *info;
+
+ info = malloc(sizeof (*info));
+ if (!info)
+ return;
+
+ memset(info, 0, sizeof(*info));
+ info->wmxsdk = wmxsdk;
+ info->media_status = media_status;
+
+ wmxsdk_ref(wmxsdk);
+ g_idle_add(media_status_change_handler, info);
+}
+
+typedef struct {
+ struct wmxsdk *wmxsdk;
+ WIMAX_API_NETWORK_CONNECTION_RESP result;
+} ConnectResultInfo;
+
+static gboolean
+connect_result_handler(gpointer user_data)
+{
+ ConnectResultInfo *info = user_data;
+
+ if (info->wmxsdk->connect_result_cb) {
+ info->wmxsdk->connect_result_cb(info->wmxsdk,
+ info->result,
+ info->wmxsdk->callback_data);
+ }
+ wmxsdk_unref(info->wmxsdk);
+ memset(info, 0, sizeof(*info));
+ free(info);
+ return FALSE;
+}
+
+static void
+_schedule_connect_result(struct wmxsdk *wmxsdk,
+ WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+ ConnectResultInfo *info;
+
+ info = malloc(sizeof (*info));
+ if (!info)
+ return;
+
+ memset(info, 0, sizeof(*info));
+ info->wmxsdk = wmxsdk;
+ info->result = resp;
+
+ wmxsdk_ref(wmxsdk);
+ g_idle_add(connect_result_handler, info);
+}
+
+typedef struct {
+ struct wmxsdk *wmxsdk;
+ WIMAX_API_NSP_INFO_EX *nsps;
+ guint num_nsps;
+} ScanResultInfo;
+
+static gboolean
+scan_result_handler(gpointer user_data)
+{
+ ScanResultInfo *info = user_data;
+
+ if (info->wmxsdk->scan_result_cb) {
+ info->wmxsdk->scan_result_cb(info->wmxsdk,
+ info->nsps,
+ info->num_nsps,
+ info->wmxsdk->callback_data);
+ }
+ wmxsdk_unref(info->wmxsdk);
+ free(info->nsps);
+ memset(info, 0, sizeof(*info));
+ free(info);
+ return FALSE;
+}
+
+static void
+_schedule_scan_result(struct wmxsdk *wmxsdk,
+ WIMAX_API_NSP_INFO_EX *nsps,
+ guint num_nsps)
+{
+ ScanResultInfo *info;
+ size_t nsps_size;
+ int i, tmp;
+
+ info = malloc(sizeof (*info));
+ if (!info)
+ return;
+
+ memset(info, 0, sizeof(*info));
+ info->wmxsdk = wmxsdk;
+
+ nsps_size = num_nsps * sizeof (WIMAX_API_NSP_INFO_EX);
+ info->nsps = malloc(nsps_size);
+ memcpy(info->nsps, nsps, nsps_size);
+ info->num_nsps = num_nsps;
+
+ /* CAPI may report link quality as zero -- if it does check if it is a bug
+ * by computing it based on CINR. If it is different, use the computed one.
+ */
+ for (i = 0; i < num_nsps; i++) {
+ WIMAX_API_NSP_INFO_EX *nsp = &info->nsps[i];
+
+ if (nsp->linkQuality == 0) {
+ tmp = cinr_to_percentage(nsp->CINR - 10);
+ if (tmp != nsp->linkQuality)
+ nsp->linkQuality = tmp;
+ }
+ }
+
+ wmxsdk_ref(wmxsdk);
+ g_idle_add(scan_result_handler, info);
+}
+
+static gboolean
+removed_handler(gpointer user_data)
+{
+ struct wmxsdk *wmxsdk = user_data;
+
+ if (wmxsdk->removed_cb)
+ wmxsdk->removed_cb(wmxsdk, wmxsdk->callback_data);
+ wmxsdk_unref(wmxsdk);
+ return FALSE;
+}
+
+static void
+_schedule_removed(struct wmxsdk *wmxsdk)
+{
+ wmxsdk_ref(wmxsdk);
+ g_idle_add(removed_handler, wmxsdk);
+}
+
+/****************************************************************/
+
+/*
+ * Convert a WiMAX API status to an string.
+ */
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
+{
+ switch (status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ return "uninitialized";
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+ return "rf off";
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+ return "rf off (hard-block)";
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ return "rf off (soft-block)";
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ return "ready";
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ return "scanning";
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ return "connecting";
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ return "connected";
+ default:
+ return "unknown";
+ }
+}
+
+const char *iwmx_sdk_reason_to_str(WIMAX_API_STATUS_REASON reason)
+{
+ switch (reason) {
+ case WIMAX_API_STATUS_REASON_Normal:
+ return "normal";
+
+ /**< Failed to complete NW entry with the selected operator (unspecified reason). */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_to_NW:
+ return "unspecified failure";
+
+ /**< Failed to complete ranging */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_Ranging:
+ return "ranging failed";
+
+ /**< SBC phase failed */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_SBC:
+ return "sbc failed";
+
+ /**< Security error. EAP authentication failed device level */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_EAP_AUTH_Device:
+ return "EAP device auth failed";
+
+ /**< Security error. EAP authentication failed user level */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_EAP_AUTH_user:
+ return "EAP user auth failed";
+
+ /**< Security error. Handshake failed */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_3_Way_Handshake:
+ return "3 way handshake failed";
+
+ /**< Registration failed */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_REG:
+ return "registration failed";
+
+ /**< Failed to initialize the data path (failed to perform DSA to one UL and one DL SFs). */
+ case WIMAX_API_STATUS_REASON_Fail_to_connect_datapath:
+ return "datapath failed";
+
+ default:
+ return "unknown";
+ }
+}
+
+const char *iwmx_sdk_media_status_to_str(WIMAX_API_MEDIA_STATUS status)
+{
+ switch (status) {
+ case WIMAX_API_MEDIA_STATUS_LINK_UP:
+ return "link-up";
+ case WIMAX_API_MEDIA_STATUS_LINK_DOWN:
+ return "link-down";
+ case WIMAX_API_MEDIA_STATUS_LINK_RENEW:
+ return "link-renew";
+ default:
+ return "unknown";
+ }
+}
+
+const char *
+iwmx_sdk_con_progress_to_str(WIMAX_API_CONNECTION_PROGRESS_INFO progress)
+{
+ switch (progress) {
+
+ /**< Device is in Ranging */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Ranging:
+ return "ranging";
+
+ /**< Device is in SBC */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_SBC:
+ return "sbc";
+
+ /**< Device is in EAP authentication Device */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_EAP_authentication_Device:
+ return "eap-auth-device";
+
+ /**< Device is in EAP authentication User */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_EAP_authentication_User:
+ return "eap-auth-user";
+
+ /**< Device is in 3-way-handshake */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_3_way_handshake:
+ return "3way-handshake";
+
+ /**< Device is in Registration */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registration:
+ return "registration";
+
+ /**< Device is in De-registration */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_De_registration:
+ return "deregistration";
+
+ /**< Device is registered (operational) */
+ case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registered:
+ return "registered";
+
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Get the device's status from the device
+ *
+ * Does NOT cache the result
+ * Does NOT trigger a state change in NetworkManager
+ *
+ * Returns < 0 errno code on error, status code if ok.
+ */
+static WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
+{
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot read device state: %d (%s)", r, errstr);
+ dev_status = -EIO;
+ }
+ return dev_status;
+}
+
+/*
+ * 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)
+{
+ const char *result;
+ WIMAX_API_DEVICE_STATUS dev_status;
+
+ 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;
+}
+
+/*
+ * 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.
+ *
+ */
+WIMAX_API_CONNECTED_NSP_INFO_EX *iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
+{
+ WIMAX_API_CONNECTED_NSP_INFO_EX *nsp_info = NULL;
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ nsp_info = malloc(sizeof (*nsp_info));
+ if (!nsp_info) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: cannot allocate NSP info");
+ return NULL;
+ }
+
+ r = GetConnectedNSPEx(&wmxsdk->device_id, nsp_info);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot get connected NSP info: %d (%s)", r, errstr);
+ free (nsp_info);
+ nsp_info = NULL;
+ } else {
+ /* Migth be 0 sometimes; fix that up */
+ if (nsp_info->linkQuality == 0) {
+ int linkq_expected = cinr_to_percentage(nsp_info->CINR - 10);
+ if (linkq_expected != nsp_info->linkQuality)
+ nsp_info->linkQuality = linkq_expected;
+ }
+ }
+
+ return nsp_info;
+}
+
+/*
+ * Asks the WiMAX API to report current link statistics.
+ *
+ * Returns NULL on error.
+ *
+ */
+WIMAX_API_LINK_STATUS_INFO_EX *iwmx_sdk_get_link_status_info(struct wmxsdk *wmxsdk)
+{
+ WIMAX_API_LINK_STATUS_INFO_EX *stats = NULL;
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ /* Only report if connected */
+ if (iwmxsdk_status_get(wmxsdk) < WIMAX_API_DEVICE_STATUS_Connecting) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: cannot get link status info unless connected");
+ return NULL;
+ }
+
+ stats = malloc(sizeof (*stats));
+ if (!stats) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: cannot allocate links status info");
+ return NULL;
+ }
+
+ r = GetLinkStatusEx(&wmxsdk->device_id, stats);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot get link status info: %d (%s)", r, errstr);
+ free (stats);
+ stats = NULL;
+ }
+
+ return stats;
+}
+
+/*
+ * 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 NetworkManager
+ * internals.
+ */
+static void __iwmx_sdk_rf_state_cb(WIMAX_API_DEVICE_ID *device_id,
+ WIMAX_API_RF_STATE rf_state)
+{
+ nm_log_dbg(LOGD_WIMAX, "rf_state changed to %d", rf_state);
+}
+
+/*
+ * 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:
+ nm_log_err(LOGD_WIMAX, "wmxsdk: cannot turn on radio: hw switch is off");
+ result = -EPERM;
+ goto error_cant_do;
+ break;
+ case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+ if (rf_state == WIMAX_API_RF_OFF) {
+ result = 0;
+ nm_log_dbg(LOGD_WIMAX, "radio is already off");
+ 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:
+ if (rf_state == WIMAX_API_RF_ON) {
+ result = 0;
+ nm_log_dbg(LOGD_WIMAX, "radio is already on");
+ 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot flip radio to %d: %d (%s) [device is in state %s]",
+ 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;
+}
+
+/*
+ * Read the cached device status
+ */
+WIMAX_API_DEVICE_STATUS iwmxsdk_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;
+}
+
+/*
+ * 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 NetworkManager internals.
+ */
+static void __iwmx_sdk_connect_cb(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 = iwmxsdk_status_get(wmxsdk);
+ if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+ if (status != WIMAX_API_DEVICE_STATUS_Data_Connected) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: error: connect worked, but state"
+ " didn't change (now it is %d [%s])",
+ status,
+ iwmx_sdk_dev_status_to_str(status));
+ }
+ } else {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: failed to connect (status %d: %s)",
+ status, iwmx_sdk_dev_status_to_str(status));
+ }
+
+ _schedule_connect_result(wmxsdk, resp);
+}
+
+/*
+ * 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 NetworkManager 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
+ * NetworkManager displays the right info.
+ */
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, const char *nsp_name)
+{
+ int result = 0;
+
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+ WIMAX_API_DEVICE_STATUS dev_status;
+ char sdk_name[MAX_SIZE_OF_NSP_NAME];
+
+ g_mutex_lock(&wmxsdk->connect_mutex);
+ /* Guess what the current radio state is; if it is ON
+ * already, don't redo it. */
+ dev_status = iwmxsdk_status_get(wmxsdk);
+ if ((int) dev_status < 0) {
+ result = dev_status;
+ goto error_get_status;
+ }
+ switch (dev_status) {
+ case WIMAX_API_DEVICE_STATUS_UnInitialized:
+ nm_log_err(LOGD_WIMAX, "wmxsdk: SW BUG? HW is uninitialized");
+ 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:
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot connect: radio is off");
+ 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:
+ nm_log_dbg(LOGD_WIMAX, "Connect already pending, waiting for it");
+ result = -EINPROGRESS;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ nm_log_err(LOGD_WIMAX, "wmxsdk: BUG? need to disconnect?");
+ result = -EINVAL;
+ goto error_cant_do;
+ default:
+ g_assert(1);
+ }
+
+ /* The SDK treats the network name as wchar_t* while the contents are
+ * actually just UTF-8... WTF? Hand it a full buffer to work around
+ * boundary cases where the NSP name contains an odd # of characters.
+ */
+ memset(sdk_name, 0, sizeof (sdk_name));
+ memcpy(sdk_name, nsp_name, strlen (nsp_name));
+
+ /* Ok, do the connection, wait for a callback */
+ r = CmdConnectToNetwork(&wmxsdk->device_id, &sdk_name[0], 0, 0);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot connect to network %s: %d (%s) - device is in state '%s'",
+ nsp_name, r, errstr,
+ iwmx_sdk_get_device_status_str(wmxsdk));
+ result = -EIO;
+ }
+
+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 NetworkManager 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(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 = iwmxsdk_status_get(wmxsdk);
+ if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+ if (status == WIMAX_API_DEVICE_STATUS_Data_Connected) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: error: disconnect worked, "
+ "but state didn't change (now it is %d [%s])", status,
+ iwmx_sdk_dev_status_to_str(status));
+ }
+ } else
+ nm_log_err(LOGD_WIMAX, "wmxsdk: failed to disconnect (status %d: %s)",
+ 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 NetworkManager'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:
+ nm_log_err(LOGD_WIMAX, "wmxsdk: SW BUG? HW is uninitialized");
+ 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:
+ nm_log_dbg(LOGD_WIMAX, "Cannot disconnect, radio is off; ignoring");
+ result = 0;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Ready:
+ case WIMAX_API_DEVICE_STATUS_Scanning:
+ nm_log_dbg(LOGD_WIMAX, "Cannot disconnect, already disconnected; ignoring");
+ result = 0;
+ goto error_cant_do;
+ case WIMAX_API_DEVICE_STATUS_Connecting:
+ case WIMAX_API_DEVICE_STATUS_Data_Connected:
+ 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot disconnect from network: %d (%s)", r, errstr);
+ result = -EIO;
+ } else
+ result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+ g_mutex_unlock(&wmxsdk->connect_mutex);
+ return result;
+}
+
+/*
+ * Turn fast reconnect capability on/off
+ *
+ * This function tells wimaxd to turn fast reconnect on or off.
+ */
+int iwmx_sdk_set_fast_reconnect_enabled(struct wmxsdk *wmxsdk, int enabled)
+{
+ WIMAX_API_RET r;
+ char errstr[512];
+ UINT32 errstr_size = sizeof(errstr);
+
+ r = SetFastReconnectCapabilityStatus(&wmxsdk->device_id, !!enabled);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot set fast reconnect to %d: %d (%s)",
+ enabled, r, errstr);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void __iwmx_sdk_media_status_update_cb (WIMAX_API_DEVICE_ID_P device_id,
+ WIMAX_API_MEDIA_STATUS mediaStatus)
+{
+ struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+
+ /* Ignore redundant LINK_UP events */
+ if ( mediaStatus == WIMAX_API_MEDIA_STATUS_LINK_UP
+ && wmxsdk->media_status == WIMAX_API_MEDIA_STATUS_LINK_UP)
+ return;
+
+ wmxsdk->media_status = mediaStatus;
+
+ nm_log_dbg(LOGD_WIMAX, "wmxsdk: media status change to (%d) %s",
+ mediaStatus, iwmx_sdk_media_status_to_str (mediaStatus));
+
+ _schedule_media_status_change(wmxsdk, mediaStatus);
+}
+
+/*
+ * Callback for state change messages
+ *
+ * Just pass them to the state transition handler
+ */
+static void __iwmx_sdk_state_change_cb(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);
+ WIMAX_API_DEVICE_STATUS old_status;
+
+ nm_log_dbg(LOGD_WIMAX, "wmxsdk: state change to (%d) %s reason (%d) %s",
+ status, iwmx_sdk_dev_status_to_str (status),
+ reason, iwmx_sdk_reason_to_str (reason));
+
+ g_mutex_lock(&wmxsdk->status_mutex);
+ old_status = wmxsdk->status;
+ wmxsdk->status = status;
+ g_mutex_unlock(&wmxsdk->status_mutex);
+
+ _schedule_state_change(wmxsdk, status, old_status, reason, pi);
+}
+
+/*
+ * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
+ * are available.
+ *
+ * From here we update NetworkManager's idea of which networks are available.
+ */
+static void __iwmx_sdk_scan_common_cb(WIMAX_API_DEVICE_ID *device_id,
+ WIMAX_API_NSP_INFO_EX *nsp_list,
+ UINT32 nsp_list_size)
+{
+ struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+
+ g_mutex_lock(&wmxsdk->network_mutex);
+ _schedule_scan_result(wmxsdk, nsp_list, nsp_list_size);
+ g_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(WIMAX_API_DEVICE_ID *device_id,
+ 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(WIMAX_API_DEVICE_ID *device_id,
+ 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 and hand them back via
+ * the scan_result_cb callback.
+ */
+int iwmx_sdk_get_networks(struct wmxsdk *wmxsdk)
+{
+ int result;
+
+ UINT32 nsp_list_length = 10;
+ 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot get network list: %d (%s)", r, errstr);
+ result = -EIO;
+ goto error_scan;
+ }
+
+ if (nsp_list_length == 0) {
+ nm_log_dbg(LOGD_WIMAX, "no networks");
+ } 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
+ *
+ */
+static int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
+{
+ int result, status;
+
+ 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot open device: %d (%s)", r, errstr);
+ goto error_wimaxdeviceopen;
+ }
+
+ /*
+ * We scan in auto mode (in the background)
+ *
+ * Otherwise is messy -- if we have NetworkManager triggering a scan
+ * when we call iwmx_nm_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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot set connectin mode to manual: %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to radio change events: %u (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to state chaneg events: %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to wide scan events: %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to scan events: %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to connect events: %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to disconnect events: %d (%s)", r, errstr);
+ goto error_subscribe_disconnect;
+ }
+
+ r = SubscribeMediaStatusUpdate(&wmxsdk->device_id, __iwmx_sdk_media_status_update_cb);
+ if (r != WIMAX_API_RET_SUCCESS) {
+ GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to media status events: %d (%s)", r, errstr);
+ goto error_subscribe_media_status;
+ }
+
+ status = iwmx_sdk_get_device_status(wmxsdk);
+ if ((int) status < 0)
+ status = WIMAX_API_DEVICE_STATUS_UnInitialized;
+
+ g_mutex_lock(&wmxsdk->status_mutex);
+ wmxsdk->status = status;
+ g_mutex_unlock(&wmxsdk->status_mutex);
+
+ _schedule_state_change(wmxsdk,
+ status,
+ WIMAX_API_DEVICE_STATUS_UnInitialized,
+ WIMAX_API_STATUS_REASON_Normal,
+ WIMAX_API_DEVICE_CONNECTION_PROGRESS_Ranging);
+
+ return 0;
+
+ UnsubscribeMediaStatusUpdate(&wmxsdk->device_id);
+error_subscribe_media_status:
+ 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:
+ return result;
+}
+
+/*
+ * Called when a device is torn down
+ *
+ * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
+ * unregister from the WiMAX API.
+ */
+static void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
+{
+ UnsubscribeMediaStatusUpdate(&wmxsdk->device_id);
+ 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);
+}
+
+void iwmx_sdk_set_callbacks(struct wmxsdk *wmxsdk,
+ WimaxStateChangeFunc state_change_cb,
+ WimaxMediaStatusFunc media_status_cb,
+ WimaxConnectResultFunc connect_result_cb,
+ WimaxScanResultFunc scan_result_cb,
+ WimaxRemovedFunc removed_cb,
+ void *user_data)
+{
+ wmxsdk->state_change_cb = state_change_cb;
+ wmxsdk->media_status_cb = media_status_cb;
+ wmxsdk->connect_result_cb = connect_result_cb;
+ wmxsdk->scan_result_cb = scan_result_cb;
+ wmxsdk->removed_cb = removed_cb;
+ wmxsdk->callback_data = user_data;
+}
+
+/* Initialize a [zeroed] struct wmxsdk */
+static struct wmxsdk *wmxsdk_new(void)
+{
+ struct wmxsdk *wmxsdk;
+
+ wmxsdk = malloc(sizeof(*wmxsdk));
+ if (wmxsdk) {
+ memset(wmxsdk, 0, sizeof(*wmxsdk));
+
+ wmxsdk->refcount = 1;
+ g_mutex_init(&wmxsdk->network_mutex);
+
+ wmxsdk->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
+ g_mutex_init(&wmxsdk->status_mutex);
+
+ g_mutex_init(&wmxsdk->connect_mutex);
+ }
+ return wmxsdk;
+}
+
+struct wmxsdk *wmxsdk_ref(struct wmxsdk *wmxsdk)
+{
+ g_atomic_int_add(&wmxsdk->refcount, 1);
+ return wmxsdk;
+}
+
+void wmxsdk_unref(struct wmxsdk *wmxsdk)
+{
+ if (g_atomic_int_dec_and_test(&wmxsdk->refcount)) {
+ g_mutex_clear(&wmxsdk->status_mutex);
+ g_mutex_clear(&wmxsdk->connect_mutex);
+ memset(wmxsdk, 0, sizeof(*wmxsdk));
+ free(wmxsdk);
+ }
+}
+
+static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
+{
+ struct wmxsdk *wmxsdk;
+ const char *s;
+
+ if (idx >= IWMX_SDK_DEV_MAX) {
+ nm_log_err(LOGD_WIMAX, "BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)", idx, IWMX_SDK_DEV_MAX);
+ return;
+ }
+ if (g_iwmx_sdk_devs[idx] != NULL) {
+ nm_log_err(LOGD_WIMAX, "BUG! device index %u already enumerated?", idx);
+ return;
+ }
+
+ wmxsdk = wmxsdk_new();
+ if (wmxsdk == NULL) {
+ nm_log_err(LOGD_WIMAX, "Can't allocate %zu bytes", sizeof(*wmxsdk));
+ return;
+ }
+
+ /*
+ * 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) {
+ nm_log_err(LOGD_WIMAX, "Cannot extract network interface name off '%s'",
+ name);
+ goto error;
+ }
+ nm_log_dbg(LOGD_WIMAX, "network interface name: '%s'", wmxsdk->ifname);
+
+ strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
+ wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+ wmxsdk->device_id.deviceIndex = api_idx;
+
+ if (iwmx_sdk_setup(wmxsdk) != 0) {
+ nm_log_err(LOGD_WIMAX, "wxmsdk: %s: cannot set up interface", wmxsdk->ifname);
+ goto error;
+ }
+
+ g_iwmx_sdk_devs[idx] = wmxsdk;
+
+ /* Notify listeners of new devices */
+ iwmx_sdk_call_new_callbacks (wmxsdk);
+ return;
+
+error:
+ wmxsdk_unref(wmxsdk);
+ return;
+}
+
+static void iwmx_sdk_dev_rm(unsigned idx)
+{
+ struct wmxsdk *wmxsdk;
+
+ if (idx >= IWMX_SDK_DEV_MAX) {
+ nm_log_err(LOGD_WIMAX, "BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)", idx, IWMX_SDK_DEV_MAX);
+ return;
+ }
+
+ wmxsdk = g_iwmx_sdk_devs[idx];
+ _schedule_removed(wmxsdk);
+ iwmx_sdk_remove(wmxsdk);
+ wmxsdk_unref(wmxsdk);
+ g_iwmx_sdk_devs[idx] = NULL;
+}
+
+static void iwmx_sdk_addremove_cb(WIMAX_API_DEVICE_ID *devid,
+ BOOL presence)
+{
+ unsigned int cnt;
+ WIMAX_API_RET r;
+ 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);
+
+ g_mutex_lock(&add_remove_mutex);
+
+ nm_log_dbg(LOGD_WIMAX, "cb: handle %u index #%u is %d", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot obtain list of devices: %d (%s)", r, errstr);
+ goto out;
+ }
+
+ if (device_id_list_size == 0) {
+ nm_log_dbg(LOGD_WIMAX, "No WiMAX devices reported");
+ } else {
+ for (cnt = 0; cnt < device_id_list_size; cnt++) {
+ WIMAX_API_HW_DEVICE_ID *dev =
+ device_id_list + cnt;
+ nm_log_dbg(LOGD_WIMAX, "#%u index #%u device %s", cnt,
+ dev->deviceIndex, dev->deviceName);
+ }
+ }
+
+ if (presence) {
+ WIMAX_API_HW_DEVICE_ID *dev;
+
+ /* Make sure the wimax NS isn't lying to us */
+ if (device_id_list_size < devid->deviceIndex) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: changed device (%u) not in the list? (%u items)",
+ devid->deviceIndex, device_id_list_size);
+ goto out;
+ }
+
+ /* Add the device to our internal list */
+ dev = device_id_list + devid->deviceIndex;
+ iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex, dev->deviceName);
+ } else {
+ /* Remove the device from our internal list */
+ int idx = deviceid_to_index(devid);
+
+ if (idx >= 0)
+ iwmx_sdk_dev_rm(idx);
+ }
+
+out:
+ g_mutex_unlock(&add_remove_mutex);
+}
+
+/*
+ * 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);
+
+ 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: WiMaxAPIOpen failed with %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: insert/remove subscribe failed with %d (%s)", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot obtain list of devices: %d (%s)", r, errstr);
+ goto error_close;
+ }
+ if (device_id_list_size < g_api.deviceIndex) {
+ nm_log_err(LOGD_WIMAX, "wmxsdk: changed device (%u) not in the list? (%u items)",
+ g_api.deviceIndex, device_id_list_size);
+ }
+
+ if (device_id_list_size == 0) {
+ nm_log_dbg(LOGD_WIMAX, "No WiMAX devices reported");
+ } else {
+ for (cnt = 0; cnt < device_id_list_size; cnt++) {
+ WIMAX_API_HW_DEVICE_ID *dev = device_id_list + cnt;
+ nm_log_dbg(LOGD_WIMAX, "#%u index #%u device %s", 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);
+ nm_log_err(LOGD_WIMAX, "wmxsdk: WiMaxAPIClose failed with %d (%s)", r, errstr);
+ }
+ return;
+}