chan.c 3.4 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * This file contains helper code to handle channel
 * settings and keeping track of what is possible at
 * any point in time.
 *
 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
 */

9
#include <linux/export.h>
10 11 12
#include <net/cfg80211.h>
#include "core.h"

13 14
struct ieee80211_channel *
rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
15 16 17 18 19 20 21 22 23
		  int freq, enum nl80211_channel_type channel_type)
{
	struct ieee80211_channel *chan;
	struct ieee80211_sta_ht_cap *ht_cap;

	chan = ieee80211_get_channel(&rdev->wiphy, freq);

	/* Primary channel not allowed */
	if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
24
		return NULL;
25 26 27

	if (channel_type == NL80211_CHAN_HT40MINUS &&
	    chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
28
		return NULL;
29 30
	else if (channel_type == NL80211_CHAN_HT40PLUS &&
		 chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
31
		return NULL;
32 33 34 35 36

	ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;

	if (channel_type != NL80211_CHAN_NO_HT) {
		if (!ht_cap->ht_supported)
37
			return NULL;
38

39 40 41
		if (channel_type != NL80211_CHAN_HT20 &&
		    (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
42
			return NULL;
43 44
	}

45 46 47
	return chan;
}

48
bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
49 50
				  struct ieee80211_channel *chan,
				  enum nl80211_channel_type channel_type)
51 52 53 54 55 56 57
{
	struct ieee80211_channel *sec_chan;
	int diff;

	switch (channel_type) {
	case NL80211_CHAN_HT40PLUS:
		diff = 20;
58
		break;
59 60
	case NL80211_CHAN_HT40MINUS:
		diff = -20;
61
		break;
62
	default:
63
		return true;
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	}

	sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff);
	if (!sec_chan)
		return false;

	/* we'll need a DFS capability later */
	if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
			       IEEE80211_CHAN_PASSIVE_SCAN |
			       IEEE80211_CHAN_NO_IBSS |
			       IEEE80211_CHAN_RADAR))
		return false;

	return true;
}
79
EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
80

81 82
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
				 int freq, enum nl80211_channel_type chantype)
83 84 85
{
	struct ieee80211_channel *chan;

86
	if (!rdev->ops->set_monitor_channel)
87
		return -EOPNOTSUPP;
88 89
	if (!cfg80211_has_monitors_only(rdev))
		return -EBUSY;
90

91
	chan = rdev_freq_to_chan(rdev, freq, chantype);
92 93 94
	if (!chan)
		return -EINVAL;

95
	return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype);
96
}
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

void
cfg80211_get_chan_state(struct cfg80211_registered_device *rdev,
		        struct wireless_dev *wdev,
		        struct ieee80211_channel **chan,
		        enum cfg80211_chan_mode *chanmode)
{
	*chan = NULL;
	*chanmode = CHAN_MODE_UNDEFINED;

	ASSERT_RDEV_LOCK(rdev);
	ASSERT_WDEV_LOCK(wdev);

	if (!netif_running(wdev->netdev))
		return;

	switch (wdev->iftype) {
	case NL80211_IFTYPE_ADHOC:
		if (wdev->current_bss) {
			*chan = wdev->current_bss->pub.channel;
			*chanmode = wdev->ibss_fixed
				  ? CHAN_MODE_SHARED
				  : CHAN_MODE_EXCLUSIVE;
			return;
		}
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
		if (wdev->current_bss) {
			*chan = wdev->current_bss->pub.channel;
			*chanmode = CHAN_MODE_SHARED;
			return;
		}
		break;
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_P2P_GO:
	case NL80211_IFTYPE_MESH_POINT:
		*chan = wdev->channel;
		*chanmode = CHAN_MODE_SHARED;
		return;
	case NL80211_IFTYPE_MONITOR:
	case NL80211_IFTYPE_AP_VLAN:
	case NL80211_IFTYPE_WDS:
		/* these interface types don't really have a channel */
		return;
	case NL80211_IFTYPE_UNSPECIFIED:
	case NUM_NL80211_IFTYPES:
		WARN_ON(1);
	}

	return;
}