#include <linux/debugfs.h>
#include <linux/notifier.h>
#include <linux/device.h>
+#include <linux/rtnetlink.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "nl80211.h"
return 0;
}
+static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
+{
+ struct cfg80211_registered_device *drv = data;
+
+ drv->ops->rfkill_poll(&drv->wiphy);
+}
+
+static int cfg80211_rfkill_set_block(void *data, bool blocked)
+{
+ struct cfg80211_registered_device *drv = data;
+ struct wireless_dev *wdev;
+
+ if (!blocked)
+ return 0;
+
+ rtnl_lock();
+ mutex_lock(&drv->devlist_mtx);
+
+ list_for_each_entry(wdev, &drv->netdev_list, list)
+ dev_close(wdev->netdev);
+
+ mutex_unlock(&drv->devlist_mtx);
+ rtnl_unlock();
+
+ return 0;
+}
+
+static void cfg80211_rfkill_sync_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *drv;
+
+ drv = container_of(work, struct cfg80211_registered_device, rfkill_sync);
+ cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill));
+}
+
/* exported functions */
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
drv->wiphy.dev.class = &ieee80211_class;
drv->wiphy.dev.platform_data = drv;
+ drv->rfkill_ops.set_block = cfg80211_rfkill_set_block;
+ drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev),
+ &drv->wiphy.dev, RFKILL_TYPE_WLAN,
+ &drv->rfkill_ops, drv);
+
+ if (!drv->rfkill) {
+ kfree(drv);
+ return NULL;
+ }
+
+ INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work);
+ INIT_WORK(&drv->conn_work, cfg80211_conn_work);
+
/*
* Initialize wiphy parameters to IEEE 802.11 MIB default values.
* Fragmentation and RTS threshold are disabled by default with the
int i;
u16 ifmodes = wiphy->interface_modes;
- if (WARN_ON(wiphy->max_scan_ssids < 1))
- return -EINVAL;
-
/* sanity check ifmodes */
WARN_ON(!ifmodes);
ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
+ res = device_add(&drv->wiphy.dev);
+ if (res)
+ return res;
+
+ res = rfkill_register(drv->rfkill);
+ if (res)
+ goto out_rm_dev;
+
mutex_lock(&cfg80211_mutex);
/* set up regulatory info */
wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
- res = device_add(&drv->wiphy.dev);
- if (res)
- goto out_unlock;
-
list_add(&drv->list, &cfg80211_drv_list);
+ mutex_unlock(&cfg80211_mutex);
+
/* add to debugfs */
drv->wiphy.debugfsdir =
debugfs_create_dir(wiphy_name(&drv->wiphy),
cfg80211_debugfs_drv_add(drv);
- res = 0;
-out_unlock:
- mutex_unlock(&cfg80211_mutex);
+ return 0;
+
+ out_rm_dev:
+ device_del(&drv->wiphy.dev);
return res;
}
EXPORT_SYMBOL(wiphy_register);
+void wiphy_rfkill_start_polling(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+ if (!drv->ops->rfkill_poll)
+ return;
+ drv->rfkill_ops.poll = cfg80211_rfkill_poll;
+ rfkill_resume_polling(drv->rfkill);
+}
+EXPORT_SYMBOL(wiphy_rfkill_start_polling);
+
+void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+ rfkill_pause_polling(drv->rfkill);
+}
+EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
+
void wiphy_unregister(struct wiphy *wiphy)
{
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+ rfkill_unregister(drv->rfkill);
+
/* protect the device list */
mutex_lock(&cfg80211_mutex);
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
+ cancel_work_sync(&drv->conn_work);
+
cfg80211_debugfs_drv_del(drv);
/* If this device got a regulatory hint tell core its
void cfg80211_dev_free(struct cfg80211_registered_device *drv)
{
struct cfg80211_internal_bss *scan, *tmp;
+ rfkill_destroy(drv->rfkill);
mutex_destroy(&drv->mtx);
mutex_destroy(&drv->devlist_mtx);
list_for_each_entry_safe(scan, tmp, &drv->bss_list, list)
}
EXPORT_SYMBOL(wiphy_free);
+void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
+{
+ struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+ if (rfkill_set_hw_state(drv->rfkill, blocked))
+ schedule_work(&drv->rfkill_sync);
+}
+EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
+
static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
unsigned long state,
void *ndev)
{
struct net_device *dev = ndev;
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev;
- if (!dev->ieee80211_ptr)
- return 0;
+ if (!wdev)
+ return NOTIFY_DONE;
- rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+ rdev = wiphy_to_dev(wdev->wiphy);
- WARN_ON(dev->ieee80211_ptr->iftype == NL80211_IFTYPE_UNSPECIFIED);
+ WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED);
switch (state) {
case NETDEV_REGISTER:
mutex_lock(&rdev->devlist_mtx);
- list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list);
+ list_add(&wdev->list, &rdev->netdev_list);
if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
"phy80211")) {
printk(KERN_ERR "wireless: failed to add phy80211 "
"symlink to netdev!\n");
}
- dev->ieee80211_ptr->netdev = dev;
+ wdev->netdev = dev;
+ wdev->sme_state = CFG80211_SME_IDLE;
+ mutex_unlock(&rdev->devlist_mtx);
#ifdef CONFIG_WIRELESS_EXT
- dev->ieee80211_ptr->wext.default_key = -1;
- dev->ieee80211_ptr->wext.default_mgmt_key = -1;
+ wdev->wext.default_key = -1;
+ wdev->wext.default_mgmt_key = -1;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+ wdev->wext.ps = CONFIG_CFG80211_DEFAULT_PS_VALUE;
+ wdev->wext.ps_timeout = 500;
+ if (rdev->ops->set_power_mgmt)
+ if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
+ wdev->wext.ps,
+ wdev->wext.ps_timeout)) {
+ /* assume this means it's off */
+ wdev->wext.ps = false;
+ }
#endif
- mutex_unlock(&rdev->devlist_mtx);
break;
case NETDEV_GOING_DOWN:
- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ cfg80211_leave_ibss(rdev, dev, true);
break;
- if (!dev->ieee80211_ptr->ssid_len)
+ case NL80211_IFTYPE_STATION:
+#ifdef CONFIG_WIRELESS_EXT
+ kfree(wdev->wext.ie);
+ wdev->wext.ie = NULL;
+ wdev->wext.ie_len = 0;
+ wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+ cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ cfg80211_mlme_down(rdev, dev);
break;
- cfg80211_leave_ibss(rdev, dev, true);
+ default:
+ break;
+ }
break;
case NETDEV_UP:
#ifdef CONFIG_WIRELESS_EXT
- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ if (wdev->wext.ibss.ssid_len)
+ cfg80211_join_ibss(rdev, dev,
+ &wdev->wext.ibss);
break;
- if (!dev->ieee80211_ptr->wext.ibss.ssid_len)
+ case NL80211_IFTYPE_STATION:
+ if (wdev->wext.connect.ssid_len)
+ cfg80211_connect(rdev, dev,
+ &wdev->wext.connect);
break;
- cfg80211_join_ibss(rdev, dev, &dev->ieee80211_ptr->wext.ibss);
- break;
+ default:
+ break;
+ }
#endif
+ break;
case NETDEV_UNREGISTER:
mutex_lock(&rdev->devlist_mtx);
- if (!list_empty(&dev->ieee80211_ptr->list)) {
+ if (!list_empty(&wdev->list)) {
sysfs_remove_link(&dev->dev.kobj, "phy80211");
- list_del_init(&dev->ieee80211_ptr->list);
+ list_del_init(&wdev->list);
}
mutex_unlock(&rdev->devlist_mtx);
break;
+ case NETDEV_PRE_UP:
+ if (rfkill_blocked(rdev->rfkill))
+ return notifier_from_errno(-ERFKILL);
+ break;
}
- return 0;
+ return NOTIFY_DONE;
}
static struct notifier_block cfg80211_netdev_notifier = {