Blame view
net/wireless/chan.c
3.12 KB
59bbb6f75
|
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * 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> */ #include <net/cfg80211.h> #include "core.h" struct ieee80211_channel * |
9588bbd55
|
13 |
rdev_freq_to_chan(struct cfg80211_registered_device *rdev, |
59bbb6f75
|
14 15 16 17 |
int freq, enum nl80211_channel_type channel_type) { struct ieee80211_channel *chan; struct ieee80211_sta_ht_cap *ht_cap; |
59bbb6f75
|
18 19 20 21 22 |
chan = ieee80211_get_channel(&rdev->wiphy, freq); /* Primary channel not allowed */ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
9588bbd55
|
23 |
return NULL; |
59bbb6f75
|
24 25 26 |
if (channel_type == NL80211_CHAN_HT40MINUS && chan->flags & IEEE80211_CHAN_NO_HT40MINUS) |
9588bbd55
|
27 |
return NULL; |
59bbb6f75
|
28 29 |
else if (channel_type == NL80211_CHAN_HT40PLUS && chan->flags & IEEE80211_CHAN_NO_HT40PLUS) |
9588bbd55
|
30 |
return NULL; |
59bbb6f75
|
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
|
36 |
return NULL; |
59bbb6f75
|
37 |
|
aed8e1f99
|
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
|
41 |
return NULL; |
59bbb6f75
|
42 |
} |
9588bbd55
|
43 44 |
return chan; } |
9236d838c
|
45 46 47 48 49 50 51 52 53 54 |
static bool can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct ieee80211_channel *sec_chan; int diff; switch (channel_type) { case NL80211_CHAN_HT40PLUS: diff = 20; |
09a02fdb9
|
55 |
break; |
9236d838c
|
56 57 |
case NL80211_CHAN_HT40MINUS: diff = -20; |
09a02fdb9
|
58 |
break; |
9236d838c
|
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; } |
f444de05d
|
76 77 78 |
int cfg80211_set_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int freq, enum nl80211_channel_type channel_type) |
9588bbd55
|
79 80 81 |
{ struct ieee80211_channel *chan; int result; |
9fbc630c8
|
82 |
if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR) |
f444de05d
|
83 84 85 86 87 88 89 90 |
wdev = NULL; if (wdev) { ASSERT_WDEV_LOCK(wdev); if (!netif_running(wdev->netdev)) return -ENETDOWN; } |
9588bbd55
|
91 92 93 94 95 96 97 |
if (!rdev->ops->set_channel) return -EOPNOTSUPP; chan = rdev_freq_to_chan(rdev, freq, channel_type); if (!chan) return -EINVAL; |
9236d838c
|
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
/* 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: if (!can_beacon_sec_chan(&rdev->wiphy, chan, channel_type)) { printk(KERN_DEBUG "cfg80211: Secondary channel not " "allowed to initiate communication "); return -EINVAL; } break; default: break; } } |
f444de05d
|
120 121 122 |
result = rdev->ops->set_channel(&rdev->wiphy, wdev ? wdev->netdev : NULL, chan, channel_type); |
59bbb6f75
|
123 124 |
if (result) return result; |
f444de05d
|
125 126 |
if (wdev) wdev->channel = chan; |
59bbb6f75
|
127 128 129 |
return 0; } |