1 /* 2 * This file contains helper code to handle channel 3 * settings and keeping track of what is possible at 4 * any point in time. 5 * 6 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 7 */ 8 9 #include <net/cfg80211.h> 10 #include "core.h" 11 12 struct ieee80211_channel * 13 rdev_freq_to_chan(struct cfg80211_registered_device *rdev, 14 int freq, enum nl80211_channel_type channel_type) 15 { 16 struct ieee80211_channel *chan; 17 struct ieee80211_sta_ht_cap *ht_cap; 18 19 chan = ieee80211_get_channel(&rdev->wiphy, freq); 20 21 /* Primary channel not allowed */ 22 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) 23 return NULL; 24 25 if (channel_type == NL80211_CHAN_HT40MINUS && 26 chan->flags & IEEE80211_CHAN_NO_HT40MINUS) 27 return NULL; 28 else if (channel_type == NL80211_CHAN_HT40PLUS && 29 chan->flags & IEEE80211_CHAN_NO_HT40PLUS) 30 return NULL; 31 32 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; 33 34 if (channel_type != NL80211_CHAN_NO_HT) { 35 if (!ht_cap->ht_supported) 36 return NULL; 37 38 if (channel_type != NL80211_CHAN_HT20 && 39 (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || 40 ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) 41 return NULL; 42 } 43 44 return chan; 45 } 46 47 static bool can_beacon_sec_chan(struct wiphy *wiphy, 48 struct ieee80211_channel *chan, 49 enum nl80211_channel_type channel_type) 50 { 51 struct ieee80211_channel *sec_chan; 52 int diff; 53 54 switch (channel_type) { 55 case NL80211_CHAN_HT40PLUS: 56 diff = 20; 57 break; 58 case NL80211_CHAN_HT40MINUS: 59 diff = -20; 60 break; 61 default: 62 return false; 63 } 64 65 sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); 66 if (!sec_chan) 67 return false; 68 69 /* we'll need a DFS capability later */ 70 if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | 71 IEEE80211_CHAN_PASSIVE_SCAN | 72 IEEE80211_CHAN_NO_IBSS | 73 IEEE80211_CHAN_RADAR)) 74 return false; 75 76 return true; 77 } 78 79 int cfg80211_set_freq(struct cfg80211_registered_device *rdev, 80 struct wireless_dev *wdev, int freq, 81 enum nl80211_channel_type channel_type) 82 { 83 struct ieee80211_channel *chan; 84 int result; 85 86 if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR) 87 wdev = NULL; 88 89 if (wdev) { 90 ASSERT_WDEV_LOCK(wdev); 91 92 if (!netif_running(wdev->netdev)) 93 return -ENETDOWN; 94 } 95 96 if (!rdev->ops->set_channel) 97 return -EOPNOTSUPP; 98 99 chan = rdev_freq_to_chan(rdev, freq, channel_type); 100 if (!chan) 101 return -EINVAL; 102 103 /* Both channels should be able to initiate communication */ 104 if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC || 105 wdev->iftype == NL80211_IFTYPE_AP || 106 wdev->iftype == NL80211_IFTYPE_AP_VLAN || 107 wdev->iftype == NL80211_IFTYPE_MESH_POINT || 108 wdev->iftype == NL80211_IFTYPE_P2P_GO)) { 109 switch (channel_type) { 110 case NL80211_CHAN_HT40PLUS: 111 case NL80211_CHAN_HT40MINUS: 112 if (!can_beacon_sec_chan(&rdev->wiphy, chan, 113 channel_type)) { 114 printk(KERN_DEBUG 115 "cfg80211: Secondary channel not " 116 "allowed to initiate communication\n"); 117 return -EINVAL; 118 } 119 break; 120 default: 121 break; 122 } 123 } 124 125 result = rdev->ops->set_channel(&rdev->wiphy, 126 wdev ? wdev->netdev : NULL, 127 chan, channel_type); 128 if (result) 129 return result; 130 131 if (wdev) 132 wdev->channel = chan; 133 134 return 0; 135 } 136