cfg80211/mac80211: better channel handling
[linux-2.6.git] / net / wireless / chan.c
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 (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
39                     ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
40                         return NULL;
41         }
42
43         return chan;
44 }
45
46 int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
47                       struct wireless_dev *wdev, int freq,
48                       enum nl80211_channel_type channel_type)
49 {
50         struct ieee80211_channel *chan;
51         int result;
52
53         if (wdev->iftype == NL80211_IFTYPE_MONITOR)
54                 wdev = NULL;
55
56         if (wdev) {
57                 ASSERT_WDEV_LOCK(wdev);
58
59                 if (!netif_running(wdev->netdev))
60                         return -ENETDOWN;
61         }
62
63         if (!rdev->ops->set_channel)
64                 return -EOPNOTSUPP;
65
66         chan = rdev_freq_to_chan(rdev, freq, channel_type);
67         if (!chan)
68                 return -EINVAL;
69
70         result = rdev->ops->set_channel(&rdev->wiphy,
71                                         wdev ? wdev->netdev : NULL,
72                                         chan, channel_type);
73         if (result)
74                 return result;
75
76         if (wdev)
77                 wdev->channel = chan;
78
79         return 0;
80 }