Blame view

net/wireless/chan.c 3.21 KB
59bbb6f75   Johannes Berg   cfg80211: validat...
1
2
3
4
5
6
7
  /*
   * 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>
   */
54858ee5b   Alexander Simon   nl80211: Parse ch...
8
  #include <linux/export.h>
59bbb6f75   Johannes Berg   cfg80211: validat...
9
10
11
12
  #include <net/cfg80211.h>
  #include "core.h"
  
  struct ieee80211_channel *
9588bbd55   Jouni Malinen   cfg80211: add rem...
13
  rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
59bbb6f75   Johannes Berg   cfg80211: validat...
14
15
16
17
  		  int freq, enum nl80211_channel_type channel_type)
  {
  	struct ieee80211_channel *chan;
  	struct ieee80211_sta_ht_cap *ht_cap;
59bbb6f75   Johannes Berg   cfg80211: validat...
18
19
20
21
22
  
  	chan = ieee80211_get_channel(&rdev->wiphy, freq);
  
  	/* Primary channel not allowed */
  	if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
9588bbd55   Jouni Malinen   cfg80211: add rem...
23
  		return NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
24
25
26
  
  	if (channel_type == NL80211_CHAN_HT40MINUS &&
  	    chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
9588bbd55   Jouni Malinen   cfg80211: add rem...
27
  		return NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
28
29
  	else if (channel_type == NL80211_CHAN_HT40PLUS &&
  		 chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
9588bbd55   Jouni Malinen   cfg80211: add rem...
30
  		return NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
31
32
33
34
35
  
  	ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
  
  	if (channel_type != NL80211_CHAN_NO_HT) {
  		if (!ht_cap->ht_supported)
9588bbd55   Jouni Malinen   cfg80211: add rem...
36
  			return NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
37

aed8e1f99   Helmut Schaa   cfg80211: don't r...
38
39
40
  		if (channel_type != NL80211_CHAN_HT20 &&
  		    (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
  		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
9588bbd55   Jouni Malinen   cfg80211: add rem...
41
  			return NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
42
  	}
9588bbd55   Jouni Malinen   cfg80211: add rem...
43
44
  	return chan;
  }
54858ee5b   Alexander Simon   nl80211: Parse ch...
45
46
47
  int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
  				  struct ieee80211_channel *chan,
  				  enum nl80211_channel_type channel_type)
9236d838c   Luis R. Rodriguez   cfg80211: fix ext...
48
49
50
51
52
53
54
  {
  	struct ieee80211_channel *sec_chan;
  	int diff;
  
  	switch (channel_type) {
  	case NL80211_CHAN_HT40PLUS:
  		diff = 20;
09a02fdb9   Mark Mentovai   cfg80211: fix can...
55
  		break;
9236d838c   Luis R. Rodriguez   cfg80211: fix ext...
56
57
  	case NL80211_CHAN_HT40MINUS:
  		diff = -20;
09a02fdb9   Mark Mentovai   cfg80211: fix can...
58
  		break;
9236d838c   Luis R. Rodriguez   cfg80211: fix ext...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  	default:
  		return false;
  	}
  
  	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;
  }
54858ee5b   Alexander Simon   nl80211: Parse ch...
76
  EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
9236d838c   Luis R. Rodriguez   cfg80211: fix ext...
77

f444de05d   Johannes Berg   cfg80211/mac80211...
78
79
80
  int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
  		      struct wireless_dev *wdev, int freq,
  		      enum nl80211_channel_type channel_type)
9588bbd55   Jouni Malinen   cfg80211: add rem...
81
82
83
  {
  	struct ieee80211_channel *chan;
  	int result;
9fbc630c8   Felix Fietkau   cfg80211: fix cra...
84
  	if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR)
f444de05d   Johannes Berg   cfg80211/mac80211...
85
86
87
88
89
90
91
92
  		wdev = NULL;
  
  	if (wdev) {
  		ASSERT_WDEV_LOCK(wdev);
  
  		if (!netif_running(wdev->netdev))
  			return -ENETDOWN;
  	}
9588bbd55   Jouni Malinen   cfg80211: add rem...
93
94
95
96
97
98
99
  
  	if (!rdev->ops->set_channel)
  		return -EOPNOTSUPP;
  
  	chan = rdev_freq_to_chan(rdev, freq, channel_type);
  	if (!chan)
  		return -EINVAL;
9236d838c   Luis R. Rodriguez   cfg80211: fix ext...
100
101
102
103
104
105
106
107
108
  	/* Both channels should be able to initiate communication */
  	if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC ||
  		     wdev->iftype == NL80211_IFTYPE_AP ||
  		     wdev->iftype == NL80211_IFTYPE_AP_VLAN ||
  		     wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
  		     wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
  		switch (channel_type) {
  		case NL80211_CHAN_HT40PLUS:
  		case NL80211_CHAN_HT40MINUS:
54858ee5b   Alexander Simon   nl80211: Parse ch...
109
110
  			if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan,
  							  channel_type)) {
9236d838c   Luis R. Rodriguez   cfg80211: fix ext...
111
112
113
114
115
116
117
118
119
120
121
  				printk(KERN_DEBUG
  				       "cfg80211: Secondary channel not "
  				       "allowed to initiate communication
  ");
  				return -EINVAL;
  			}
  			break;
  		default:
  			break;
  		}
  	}
f444de05d   Johannes Berg   cfg80211/mac80211...
122
123
124
  	result = rdev->ops->set_channel(&rdev->wiphy,
  					wdev ? wdev->netdev : NULL,
  					chan, channel_type);
59bbb6f75   Johannes Berg   cfg80211: validat...
125
126
  	if (result)
  		return result;
f444de05d   Johannes Berg   cfg80211/mac80211...
127
128
  	if (wdev)
  		wdev->channel = chan;
59bbb6f75   Johannes Berg   cfg80211: validat...
129
130
131
  
  	return 0;
  }