cfg80211: add cfg80211 exported function tracing
[platform/kernel/linux-rpi.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 <linux/export.h>
10 #include <net/cfg80211.h>
11 #include "core.h"
12 #include "rdev-ops.h"
13
14 struct ieee80211_channel *
15 rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
16                   int freq, enum nl80211_channel_type channel_type)
17 {
18         struct ieee80211_channel *chan;
19         struct ieee80211_sta_ht_cap *ht_cap;
20
21         chan = ieee80211_get_channel(&rdev->wiphy, freq);
22
23         /* Primary channel not allowed */
24         if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
25                 return NULL;
26
27         if (channel_type == NL80211_CHAN_HT40MINUS &&
28             chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
29                 return NULL;
30         else if (channel_type == NL80211_CHAN_HT40PLUS &&
31                  chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
32                 return NULL;
33
34         ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
35
36         if (channel_type != NL80211_CHAN_NO_HT) {
37                 if (!ht_cap->ht_supported)
38                         return NULL;
39
40                 if (channel_type != NL80211_CHAN_HT20 &&
41                     (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
42                     ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
43                         return NULL;
44         }
45
46         return chan;
47 }
48
49 bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
50                                   struct ieee80211_channel *chan,
51                                   enum nl80211_channel_type channel_type)
52 {
53         struct ieee80211_channel *sec_chan;
54         int diff;
55
56         trace_cfg80211_can_beacon_sec_chan(wiphy, chan, channel_type);
57
58         switch (channel_type) {
59         case NL80211_CHAN_HT40PLUS:
60                 diff = 20;
61                 break;
62         case NL80211_CHAN_HT40MINUS:
63                 diff = -20;
64                 break;
65         default:
66                 trace_cfg80211_return_bool(true);
67                 return true;
68         }
69
70         sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff);
71         if (!sec_chan) {
72                 trace_cfg80211_return_bool(false);
73                 return false;
74         }
75
76         /* we'll need a DFS capability later */
77         if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
78                                IEEE80211_CHAN_PASSIVE_SCAN |
79                                IEEE80211_CHAN_NO_IBSS |
80                                IEEE80211_CHAN_RADAR)) {
81                 trace_cfg80211_return_bool(false);
82                 return false;
83         }
84         trace_cfg80211_return_bool(true);
85         return true;
86 }
87 EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
88
89 int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
90                                  int freq, enum nl80211_channel_type chantype)
91 {
92         struct ieee80211_channel *chan;
93
94         if (!rdev->ops->set_monitor_channel)
95                 return -EOPNOTSUPP;
96         if (!cfg80211_has_monitors_only(rdev))
97                 return -EBUSY;
98
99         chan = rdev_freq_to_chan(rdev, freq, chantype);
100         if (!chan)
101                 return -EINVAL;
102
103         return rdev_set_monitor_channel(rdev, chan, chantype);
104 }
105
106 void
107 cfg80211_get_chan_state(struct wireless_dev *wdev,
108                         struct ieee80211_channel **chan,
109                         enum cfg80211_chan_mode *chanmode)
110 {
111         *chan = NULL;
112         *chanmode = CHAN_MODE_UNDEFINED;
113
114         ASSERT_WDEV_LOCK(wdev);
115
116         if (wdev->netdev && !netif_running(wdev->netdev))
117                 return;
118
119         switch (wdev->iftype) {
120         case NL80211_IFTYPE_ADHOC:
121                 if (wdev->current_bss) {
122                         *chan = wdev->current_bss->pub.channel;
123                         *chanmode = wdev->ibss_fixed
124                                   ? CHAN_MODE_SHARED
125                                   : CHAN_MODE_EXCLUSIVE;
126                         return;
127                 }
128         case NL80211_IFTYPE_STATION:
129         case NL80211_IFTYPE_P2P_CLIENT:
130                 if (wdev->current_bss) {
131                         *chan = wdev->current_bss->pub.channel;
132                         *chanmode = CHAN_MODE_SHARED;
133                         return;
134                 }
135                 break;
136         case NL80211_IFTYPE_AP:
137         case NL80211_IFTYPE_P2P_GO:
138                 if (wdev->beacon_interval) {
139                         *chan = wdev->channel;
140                         *chanmode = CHAN_MODE_SHARED;
141                 }
142                 return;
143         case NL80211_IFTYPE_MESH_POINT:
144                 if (wdev->mesh_id_len) {
145                         *chan = wdev->channel;
146                         *chanmode = CHAN_MODE_SHARED;
147                 }
148                 return;
149         case NL80211_IFTYPE_MONITOR:
150         case NL80211_IFTYPE_AP_VLAN:
151         case NL80211_IFTYPE_WDS:
152                 /* these interface types don't really have a channel */
153                 return;
154         case NL80211_IFTYPE_P2P_DEVICE:
155                 if (wdev->wiphy->features &
156                                 NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
157                         *chanmode = CHAN_MODE_EXCLUSIVE;
158                 return;
159         case NL80211_IFTYPE_UNSPECIFIED:
160         case NUM_NL80211_IFTYPES:
161                 WARN_ON(1);
162         }
163
164         return;
165 }