blob: c2556af1eace21d4a46abe109580aabc82678001 [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2007-2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <net/if.h>
#include <glib.h>
#define CONNMAN_API_SUBJECT_TO_CHANGE
#include <connman/device.h>
#include <connman/inet.h>
#include <connman/log.h>
#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)
{
return container_of(device_id, struct wmxsdk, device_id);
}
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;
}
/*
* 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";
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";
}
}
/*
* 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)
{
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);
connman_error("wmxsdk: Cannot read device state: %d (%s)\n",
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;
}
/*
* 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];
}
/*
* 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)
{
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;
}
/*
* 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)
{
_DBG_IWMX("rf_state changed to %d\n", 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:
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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("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_IWMX("#%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_IWMX("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_IWMX("#%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;
}