diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-05-23 00:44:46 -0700 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2009-05-23 00:47:48 -0700 |
commit | 00d98e92bf5976a721caa68fc100fcc19068410b (patch) | |
tree | b5c0bad35771bdf7094795efa582a690eaa5f92a /plugins/iwmx.c | |
parent | 2fdc294cf3c903f2c292a7abaf4ab63ab37a20ac (diff) | |
download | connman-00d98e92bf5976a721caa68fc100fcc19068410b.tar.gz connman-00d98e92bf5976a721caa68fc100fcc19068410b.tar.bz2 connman-00d98e92bf5976a721caa68fc100fcc19068410b.zip |
Add plugin for Intel WiMAX SDK
Diffstat (limited to 'plugins/iwmx.c')
-rw-r--r-- | plugins/iwmx.c | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/plugins/iwmx.c b/plugins/iwmx.c new file mode 100644 index 00000000..ccbd1d21 --- /dev/null +++ b/plugins/iwmx.c @@ -0,0 +1,587 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> + +#define CONNMAN_API_SUBJECT_TO_CHANGE +#include <connman/plugin.h> +#include <connman/device.h> +#include <connman/inet.h> +#include <connman/log.h> + +#include <WiMaxAPI.h> +#include <WiMaxAPIEx.h> + +#include "iwmx.h" + +/* + * Connman plugin interface + * + * This part deals with the connman internals + */ + +/* WiMAX network driver probe/remove, nops */ +static int iwmx_cm_network_probe(struct connman_network *nw) +{ + return 0; +} + +static void iwmx_cm_network_remove(struct connman_network *nw) +{ +} + +/* + * Called by connman when it wants us to tell the device to connect to + * the network @network_el; the device is @network_el->parent. + * + * We do a synchronous call to start the connection; the logic + * attached to the status change callback will update the connman + * internals once the change happens. + */ +static int iwmx_cm_network_connect(struct connman_network *nw) +{ + int result; + struct wmxsdk *wmxsdk; + const char *station_name = connman_network_get_identifier(nw); + + wmxsdk = connman_device_get_data(connman_network_get_device(nw)); + result = iwmx_sdk_connect(wmxsdk, nw); + DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result); + return result; +} + +/* + * Called by connman to have the device @nw->parent + * disconnected from @nw. + * + * We do a synchronous call to start the disconnection; the logic + * attached to the status change callback will update the connman + * internals once the change happens. + */ +static int iwmx_cm_network_disconnect(struct connman_network *nw) +{ + int result; + struct wmxsdk *wmxsdk; + const char *station_name = connman_network_get_identifier(nw); + + wmxsdk = connman_device_get_data(connman_network_get_device(nw)); + result = iwmx_sdk_disconnect(wmxsdk); + DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result); + return 0; +} + +/* + * "Driver" for the networks detected by a device. + */ +static struct connman_network_driver iwmx_cm_network_driver = { + .name = "iwmx", + .type = CONNMAN_NETWORK_TYPE_WIMAX, + .probe = iwmx_cm_network_probe, + .remove = iwmx_cm_network_remove, + .connect = iwmx_cm_network_connect, + .disconnect = iwmx_cm_network_disconnect, +}; + +/* + * A (maybe) new network is available, create/update its data + * + * If the network is new, we create and register a new element; if it + * is not, we reuse the one in the list. + * + * NOTE: + * wmxsdk->network_mutex has to be locked + */ +struct connman_network *__iwmx_cm_network_available( + struct wmxsdk *wmxsdk, const char *station_name, + const char *station_type, + const void *sdk_nspname, size_t sdk_nspname_size, + int strength) +{ + struct connman_network *nw = NULL; + struct connman_device *dev = wmxsdk->dev; + char group[3 * strlen(station_name) + 1]; + unsigned cnt; + + nw = connman_device_get_network(dev, station_name); + if (nw == NULL) { + DBG("new network %s", station_name); + nw = connman_network_create(station_name, + CONNMAN_NETWORK_TYPE_WIMAX); + connman_network_set_index(nw, connman_device_get_index(dev)); + connman_network_set_protocol(nw, CONNMAN_NETWORK_PROTOCOL_IP); + connman_network_set_name(nw, station_name); + connman_network_set_blob(nw, "WiMAX.NSP.name", + sdk_nspname, sdk_nspname_size); + /* FIXME: add roaming info? */ + /* Set the group name -- this has to be a unique + * [a-zA-Z0-9_] string common to all the networks that + * are actually the same provider. In WiMAX each + * network from the CAPI is a single provider, so we + * just set this as the network name, encoded in + * hex. */ + for (cnt = 0; station_name[cnt] != 0; cnt++) + sprintf(group + 3 * cnt, "%02x", station_name[cnt]); + group[3 * cnt + 1] = 0; + connman_network_set_group(nw, station_name); + if (connman_device_add_network(dev, nw) < 0) { + connman_network_unref(nw); + goto error_add; + } + } else + DBG("updating network %s nw %p\n", station_name, nw); + connman_network_set_available(nw, TRUE); + connman_network_set_strength(nw, strength); + connman_network_set_string(nw, "WiMAX Network Type", station_type); +error_add: + return nw; +} + +/* + * A new network is available [locking version] + * + * See __iwmx_cm_network_available() for docs + */ +struct connman_network *iwmx_cm_network_available( + struct wmxsdk *wmxsdk, const char *station_name, + const char *station_type, + const void *sdk_nspname, size_t sdk_nspname_size, + int strength) +{ + struct connman_network *nw; + + g_static_mutex_lock(&wmxsdk->network_mutex); + nw = __iwmx_cm_network_available(wmxsdk, station_name, station_type, + sdk_nspname, sdk_nspname_size, + strength); + g_static_mutex_unlock(&wmxsdk->network_mutex); + return nw; +} + +/* + * The device has been enabled, make sure connman knows + */ +static void iwmx_cm_dev_enabled(struct wmxsdk *wmxsdk) +{ + struct connman_device *dev = wmxsdk->dev; + connman_inet_ifup(connman_device_get_index(dev)); + connman_device_set_powered(dev, TRUE); +} + +/* + * The device has been disabled, make sure connman is aware of it. + */ +static void iwmx_cm_dev_disabled(struct wmxsdk *wmxsdk) +{ + struct connman_device *dev = wmxsdk->dev; + connman_inet_ifdown(connman_device_get_index(dev)); + connman_device_set_powered(dev, FALSE); +} + +/* + * The device has been (externally to connman) connnected to a + * network, make sure connman knows. + * + * When the device is connected to a network, this function is called + * to change connman's internal state to reflect the fact. + * + * If the change came from an external entity, that means that our + * connect code wasn't called. Our connect code sets + * @wmxsdk->connecting_nw to the network we were connecting + * to. If it is unset, it means an external entity forced the device + * to connect. In that case, we need to find out which network it was + * connected to, and create/lookup a @nw for it. + * + * Once the nw is set, then we are done. + */ +static void iwmx_cm_dev_connected(struct wmxsdk *wmxsdk) +{ + struct connman_network *nw; + + g_mutex_lock(wmxsdk->connect_mutex); + nw = wmxsdk->connecting_nw; + if (nw == NULL) { + nw = __iwmx_sdk_get_connected_network(wmxsdk); + if (nw == NULL) { + connman_error("wmxsdk: can't find connected network\n"); + goto error_nw_find; + } + } + wmxsdk->nw = connman_network_ref(nw); + wmxsdk->connecting_nw = NULL; + connman_network_set_connected(nw, TRUE); + DBG("connected to network %s\n", + connman_network_get_identifier(nw)); +error_nw_find: + g_mutex_unlock(wmxsdk->connect_mutex); +} + +/* + * The device has been (externally to connman) disconnnected, make + * sure connman knows + * + * We need to reverse the steps done in iwmx_cm_dev_connected(). + * If the event was caused by an external entity and we had no record + * of being connected to a network...well, bad luck. We'll just + * pretend it happened ok. + */ +static void __iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk) +{ + struct connman_network *nw = wmxsdk->nw; + + if (nw != NULL) { + DBG("disconnected from network %s\n", + connman_network_get_identifier(nw)); + connman_network_set_connected(nw, FALSE); + connman_network_unref(nw); + wmxsdk->nw = NULL; + } else + DBG("disconnected from unknown network\n"); +} + +/* + * The device has been disconnnected, make sure connman knows + * + * See __iwmx_cm_dev_disconnect() for more information. + */ +static void iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk) +{ + g_mutex_lock(wmxsdk->connect_mutex); + __iwmx_cm_dev_disconnected(wmxsdk); + g_mutex_unlock(wmxsdk->connect_mutex); +} + +/* + * Handle a change in state + * + * This is were most of the action happens. When the device changes + * state, this will catch it (through the state change callback or an + * explicit call) and call iwmx_cm_dev_*ed() to indicate to connman what + * happened. + * + * Finally, cache the new device status. + */ +void __iwmx_cm_state_change(struct wmxsdk *wmxsdk, + WIMAX_API_DEVICE_STATUS __new_status) +{ + WIMAX_API_DEVICE_STATUS __old_status = wmxsdk->status; + WIMAX_API_DEVICE_STATUS old_status; + WIMAX_API_DEVICE_STATUS new_status; + + /* + * Simplify state transition computations. + * + * For practical effects, some states are the same + */ + + /* Conection_Idle is the same as Data_Connected */ + if (__old_status == WIMAX_API_DEVICE_STATUS_Connection_Idle) + old_status = WIMAX_API_DEVICE_STATUS_Data_Connected; + else + old_status = __old_status; + if (__new_status == WIMAX_API_DEVICE_STATUS_Connection_Idle) + new_status = WIMAX_API_DEVICE_STATUS_Data_Connected; + else + new_status = __new_status; + + /* Radio off: all are just RF_OFF_SW (the highest) */ + switch (__old_status) { + case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW: + case WIMAX_API_DEVICE_STATUS_RF_OFF_HW: + case WIMAX_API_DEVICE_STATUS_RF_OFF_SW: + old_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW; + break; + default: + old_status = __old_status; + break; + } + + switch (__new_status) { + case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW: + case WIMAX_API_DEVICE_STATUS_RF_OFF_HW: + case WIMAX_API_DEVICE_STATUS_RF_OFF_SW: + new_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW; + break; + default: + new_status = __new_status; + break; + } + + /* If no real state change, do nothing */ + if (old_status == new_status) { + DBG("no state changed\n"); + return; + } else + DBG("state change from %d (%d: %s) to %d (%d: %s)\n", + old_status, __old_status, + iwmx_sdk_dev_status_to_str(__old_status), + new_status, __new_status, + iwmx_sdk_dev_status_to_str(__new_status)); + + /* Cleanup old state */ + switch (old_status) { + case WIMAX_API_DEVICE_STATUS_UnInitialized: + /* This means the plugin is starting but the device is + * in some state already, so we need to update our + * internal knowledge of it. */ + if (new_status > WIMAX_API_DEVICE_STATUS_RF_OFF_SW) + iwmx_cm_dev_enabled(wmxsdk); + break; + case WIMAX_API_DEVICE_STATUS_RF_OFF_SW: + /* This means the radio is being turned on, so enable + * the device ( unless going to uninitialized). */ + if (new_status != WIMAX_API_DEVICE_STATUS_RF_OFF_SW) + iwmx_cm_dev_enabled(wmxsdk); + break; + case WIMAX_API_DEVICE_STATUS_Ready: + break; + case WIMAX_API_DEVICE_STATUS_Scanning: + break; + case WIMAX_API_DEVICE_STATUS_Connecting: + break; + case WIMAX_API_DEVICE_STATUS_Data_Connected: + iwmx_cm_dev_disconnected(wmxsdk); + break; + default: + connman_error("wmxsdk: unknown old status %d\n", old_status); + return; + }; + + /* Implement new state */ + switch (new_status) { + case WIMAX_API_DEVICE_STATUS_UnInitialized: + break; + case WIMAX_API_DEVICE_STATUS_RF_OFF_SW: + /* This means the radio is being turned off, so + * disable the device unless coming from uninitialized. */ + if (old_status != WIMAX_API_DEVICE_STATUS_UnInitialized) + iwmx_cm_dev_disabled(wmxsdk); + break; + case WIMAX_API_DEVICE_STATUS_Ready: + break; + case WIMAX_API_DEVICE_STATUS_Scanning: + break; + case WIMAX_API_DEVICE_STATUS_Connecting: + break; + case WIMAX_API_DEVICE_STATUS_Data_Connected: + iwmx_cm_dev_connected(wmxsdk); + break; + default: + connman_error("wmxsdk: unknown new status %d\n", old_status); + return; + }; + wmxsdk->status = __new_status; +} + +/* + * Implement a device state transition [locking version] + * + * See __iwmx_cm_state_change() + */ +void iwmx_cm_state_change(struct wmxsdk *wmxsdk, + WIMAX_API_DEVICE_STATUS __new_status) +{ + g_mutex_lock(wmxsdk->status_mutex); + __iwmx_cm_state_change(wmxsdk, __new_status); + g_mutex_unlock(wmxsdk->status_mutex); +} + +/* + * Read the cached device status + */ +WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk) +{ + WIMAX_API_DEVICE_STATUS status; + + g_mutex_lock(wmxsdk->status_mutex); + status = wmxsdk->status; + g_mutex_unlock(wmxsdk->status_mutex); + return status; +} + +/* + * Called by connman when a device is enabled by the user + * + * We need to turn the radio on; the state change function will poke + * the internals. + */ +static int iwmx_cm_enable(struct connman_device *dev) +{ + int result; + struct wmxsdk *wmxsdk = connman_device_get_data(dev); + + connman_inet_ifup(connman_device_get_index(dev)); + result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_ON); + return result; +} + +/* + * Called by connman when a device is disabled by the user + * + * Simple: just make sure the radio is off; the state change function + * will poke the internals. + */ +static int iwmx_cm_disable(struct connman_device *dev) +{ + int result; + struct wmxsdk *wmxsdk = connman_device_get_data(dev); + + result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_OFF); + connman_inet_ifdown(connman_device_get_index(dev)); + return 0; +} + +/* + * Probe deferred call from when the mainloop is idle + * + * probe() schedules this to be called from the mainloop when idle to + * do a device status evaluation. Needed because of an internal race + * condition in connman. FIXME: deploy into _probe() when fixed. + */ +static gboolean __iwmx_cm_probe_dpc(gpointer _wmxsdk) +{ + int result; + struct wmxsdk *wmxsdk = _wmxsdk; + result = iwmx_sdk_get_device_status(wmxsdk); + if (result < 0) + connman_error("wmxsdk: can't get status: %d\n", result); + else + iwmx_cm_state_change(wmxsdk, result); + return FALSE; +} + +/* + * Called by connman when a new device pops in + * + * We allocate our private structure, register with the WiMAX API, + * open their device, subscribe to all the callbacks. + * + * At the end, we launch a deferred call (to work around current + * connman issues that need to be fixed in the future) and update the + * device's status. This allows us to pick up the current status and + * adapt connman's idea of the device to it. + */ +static int iwmx_cm_probe(struct connman_device *dev) +{ + int result; + struct wmxsdk *wmxsdk = NULL; + + wmxsdk = connman_device_get_data(dev); + if (wmxsdk == NULL) + /* not called from a discovery done by the WiMAX + * Network Service, ignore */ + return -ENODEV; + + result = iwmx_sdk_setup(wmxsdk); + if (result < 0) + goto error_setup; + + /* There is a race condition in the connman core that doesn't + * allow us to call this directly and things to work properly + * FIXME FIXME FIXME: merge _dpc call in here when connman is fixed */ + g_idle_add(__iwmx_cm_probe_dpc, wmxsdk); + return 0; + + iwmx_sdk_remove(wmxsdk); +error_setup: + return result; +} + +/* + * Called when a device is removed from connman + * + * Cleanup all that is done in _probe. Remove callbacks, unregister + * from the WiMAX API. + */ +static void iwmx_cm_remove(struct connman_device *dev) +{ + struct wmxsdk *wmxsdk = connman_device_get_data(dev); + iwmx_sdk_remove(wmxsdk); +} + +/* + * Called by connman to ask the device to scan for networks + * + * We have set in the WiMAX API the scan result callbacks, so we just + * start a simple scan (not a wide one). + * + * First we obtain the current list of networks and pass it to the + * callback processor. Then we start an scan cycle. + */ +static int iwmx_cm_scan(struct connman_device *dev) +{ + struct wmxsdk *wmxsdk = connman_device_get_data(dev); + return iwmx_sdk_scan(wmxsdk); +} + +/* + * Driver for a WiMAX API based device. + */ +static struct connman_device_driver iwmx_cm_device_driver = { + .name = "iwmx", + .type = CONNMAN_DEVICE_TYPE_WIMAX, + .probe = iwmx_cm_probe, + .remove = iwmx_cm_remove, + .enable = iwmx_cm_enable, + .disable = iwmx_cm_disable, + .scan = iwmx_cm_scan, +}; + +static int iwmx_cm_init(void) +{ + int result; + + result = connman_device_driver_register(&iwmx_cm_device_driver); + if (result < 0) + goto error_driver_register; + result = connman_network_driver_register(&iwmx_cm_network_driver); + if (result < 0) + goto error_network_driver_register; + result = iwmx_sdk_api_init(); + if (result < 0) + goto error_iwmx_sdk_init; + return 0; + +error_iwmx_sdk_init: + connman_network_driver_unregister(&iwmx_cm_network_driver); +error_network_driver_register: + connman_device_driver_unregister(&iwmx_cm_device_driver); +error_driver_register: + return result; +} + +static void iwmx_cm_exit(void) +{ + iwmx_sdk_api_exit(); + connman_network_driver_unregister(&iwmx_cm_network_driver); + connman_device_driver_unregister(&iwmx_cm_device_driver); +} + +CONNMAN_PLUGIN_DEFINE(iwmx, "Intel WiMAX SDK / Common API plugin", + CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_LOW, + iwmx_cm_init, iwmx_cm_exit); |