diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/Makefile | 2 | ||||
-rw-r--r-- | net/wireless/core.c | 41 | ||||
-rw-r--r-- | net/wireless/core.h | 3 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 552 | ||||
-rw-r--r-- | net/wireless/reg.c | 162 | ||||
-rw-r--r-- | net/wireless/util.c | 121 | ||||
-rw-r--r-- | net/wireless/wext.c | 2 |
7 files changed, 859 insertions, 24 deletions
diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 65710a42e5a7..b9f943c45f3b 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | obj-$(CONFIG_WIRELESS_EXT) += wext.o | 1 | obj-$(CONFIG_WIRELESS_EXT) += wext.o |
2 | obj-$(CONFIG_CFG80211) += cfg80211.o | 2 | obj-$(CONFIG_CFG80211) += cfg80211.o |
3 | 3 | ||
4 | cfg80211-y += core.o sysfs.o radiotap.o | 4 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o |
5 | cfg80211-$(CONFIG_NL80211) += nl80211.o | 5 | cfg80211-$(CONFIG_NL80211) += nl80211.o |
diff --git a/net/wireless/core.c b/net/wireless/core.c index cfc5fc5f9e75..80afacdae46c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -232,6 +232,47 @@ int wiphy_register(struct wiphy *wiphy) | |||
232 | { | 232 | { |
233 | struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); | 233 | struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); |
234 | int res; | 234 | int res; |
235 | enum ieee80211_band band; | ||
236 | struct ieee80211_supported_band *sband; | ||
237 | bool have_band = false; | ||
238 | int i; | ||
239 | |||
240 | /* sanity check supported bands/channels */ | ||
241 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
242 | sband = wiphy->bands[band]; | ||
243 | if (!sband) | ||
244 | continue; | ||
245 | |||
246 | sband->band = band; | ||
247 | |||
248 | if (!sband->n_channels || !sband->n_bitrates) { | ||
249 | WARN_ON(1); | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | |||
253 | for (i = 0; i < sband->n_channels; i++) { | ||
254 | sband->channels[i].orig_flags = | ||
255 | sband->channels[i].flags; | ||
256 | sband->channels[i].orig_mag = | ||
257 | sband->channels[i].max_antenna_gain; | ||
258 | sband->channels[i].orig_mpwr = | ||
259 | sband->channels[i].max_power; | ||
260 | sband->channels[i].band = band; | ||
261 | } | ||
262 | |||
263 | have_band = true; | ||
264 | } | ||
265 | |||
266 | if (!have_band) { | ||
267 | WARN_ON(1); | ||
268 | return -EINVAL; | ||
269 | } | ||
270 | |||
271 | /* check and set up bitrates */ | ||
272 | ieee80211_set_bitrate_flags(wiphy); | ||
273 | |||
274 | /* set up regulatory info */ | ||
275 | wiphy_update_regulatory(wiphy); | ||
235 | 276 | ||
236 | mutex_lock(&cfg80211_drv_mutex); | 277 | mutex_lock(&cfg80211_drv_mutex); |
237 | 278 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index eb0f846b40df..7a02c356d63d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -78,4 +78,7 @@ extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); | |||
78 | extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, | 78 | extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, |
79 | char *newname); | 79 | char *newname); |
80 | 80 | ||
81 | void ieee80211_set_bitrate_flags(struct wiphy *wiphy); | ||
82 | void wiphy_update_regulatory(struct wiphy *wiphy); | ||
83 | |||
81 | #endif /* __NET_WIRELESS_CORE_H */ | 84 | #endif /* __NET_WIRELESS_CORE_H */ |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f68a5c8f2147..2bdd4dddc0e1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -81,7 +81,12 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
81 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, | 81 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, |
82 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, | 82 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, |
83 | .len = NL80211_MAX_SUPP_RATES }, | 83 | .len = NL80211_MAX_SUPP_RATES }, |
84 | [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, | ||
84 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | 85 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, |
86 | [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED }, | ||
87 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, | ||
88 | .len = IEEE80211_MAX_MESH_ID_LEN }, | ||
89 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, | ||
85 | }; | 90 | }; |
86 | 91 | ||
87 | /* message building helper */ | 92 | /* message building helper */ |
@@ -98,6 +103,13 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
98 | struct cfg80211_registered_device *dev) | 103 | struct cfg80211_registered_device *dev) |
99 | { | 104 | { |
100 | void *hdr; | 105 | void *hdr; |
106 | struct nlattr *nl_bands, *nl_band; | ||
107 | struct nlattr *nl_freqs, *nl_freq; | ||
108 | struct nlattr *nl_rates, *nl_rate; | ||
109 | enum ieee80211_band band; | ||
110 | struct ieee80211_channel *chan; | ||
111 | struct ieee80211_rate *rate; | ||
112 | int i; | ||
101 | 113 | ||
102 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); | 114 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); |
103 | if (!hdr) | 115 | if (!hdr) |
@@ -105,6 +117,73 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
105 | 117 | ||
106 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); | 118 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); |
107 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); | 119 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); |
120 | |||
121 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); | ||
122 | if (!nl_bands) | ||
123 | goto nla_put_failure; | ||
124 | |||
125 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
126 | if (!dev->wiphy.bands[band]) | ||
127 | continue; | ||
128 | |||
129 | nl_band = nla_nest_start(msg, band); | ||
130 | if (!nl_band) | ||
131 | goto nla_put_failure; | ||
132 | |||
133 | /* add frequencies */ | ||
134 | nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); | ||
135 | if (!nl_freqs) | ||
136 | goto nla_put_failure; | ||
137 | |||
138 | for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { | ||
139 | nl_freq = nla_nest_start(msg, i); | ||
140 | if (!nl_freq) | ||
141 | goto nla_put_failure; | ||
142 | |||
143 | chan = &dev->wiphy.bands[band]->channels[i]; | ||
144 | NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ, | ||
145 | chan->center_freq); | ||
146 | |||
147 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
148 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED); | ||
149 | if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) | ||
150 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN); | ||
151 | if (chan->flags & IEEE80211_CHAN_NO_IBSS) | ||
152 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS); | ||
153 | if (chan->flags & IEEE80211_CHAN_RADAR) | ||
154 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); | ||
155 | |||
156 | nla_nest_end(msg, nl_freq); | ||
157 | } | ||
158 | |||
159 | nla_nest_end(msg, nl_freqs); | ||
160 | |||
161 | /* add bitrates */ | ||
162 | nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); | ||
163 | if (!nl_rates) | ||
164 | goto nla_put_failure; | ||
165 | |||
166 | for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) { | ||
167 | nl_rate = nla_nest_start(msg, i); | ||
168 | if (!nl_rate) | ||
169 | goto nla_put_failure; | ||
170 | |||
171 | rate = &dev->wiphy.bands[band]->bitrates[i]; | ||
172 | NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE, | ||
173 | rate->bitrate); | ||
174 | if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) | ||
175 | NLA_PUT_FLAG(msg, | ||
176 | NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE); | ||
177 | |||
178 | nla_nest_end(msg, nl_rate); | ||
179 | } | ||
180 | |||
181 | nla_nest_end(msg, nl_rates); | ||
182 | |||
183 | nla_nest_end(msg, nl_band); | ||
184 | } | ||
185 | nla_nest_end(msg, nl_bands); | ||
186 | |||
108 | return genlmsg_end(msg, hdr); | 187 | return genlmsg_end(msg, hdr); |
109 | 188 | ||
110 | nla_put_failure: | 189 | nla_put_failure: |
@@ -262,12 +341,45 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) | |||
262 | return -ENOBUFS; | 341 | return -ENOBUFS; |
263 | } | 342 | } |
264 | 343 | ||
344 | static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { | ||
345 | [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, | ||
346 | [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, | ||
347 | [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, | ||
348 | [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, | ||
349 | [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, | ||
350 | }; | ||
351 | |||
352 | static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) | ||
353 | { | ||
354 | struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; | ||
355 | int flag; | ||
356 | |||
357 | *mntrflags = 0; | ||
358 | |||
359 | if (!nla) | ||
360 | return -EINVAL; | ||
361 | |||
362 | if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, | ||
363 | nla, mntr_flags_policy)) | ||
364 | return -EINVAL; | ||
365 | |||
366 | for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) | ||
367 | if (flags[flag]) | ||
368 | *mntrflags |= (1<<flag); | ||
369 | |||
370 | return 0; | ||
371 | } | ||
372 | |||
265 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | 373 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) |
266 | { | 374 | { |
267 | struct cfg80211_registered_device *drv; | 375 | struct cfg80211_registered_device *drv; |
376 | struct vif_params params; | ||
268 | int err, ifindex; | 377 | int err, ifindex; |
269 | enum nl80211_iftype type; | 378 | enum nl80211_iftype type; |
270 | struct net_device *dev; | 379 | struct net_device *dev; |
380 | u32 flags; | ||
381 | |||
382 | memset(¶ms, 0, sizeof(params)); | ||
271 | 383 | ||
272 | if (info->attrs[NL80211_ATTR_IFTYPE]) { | 384 | if (info->attrs[NL80211_ATTR_IFTYPE]) { |
273 | type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); | 385 | type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); |
@@ -287,8 +399,18 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
287 | goto unlock; | 399 | goto unlock; |
288 | } | 400 | } |
289 | 401 | ||
402 | if (type == NL80211_IFTYPE_MESH_POINT && | ||
403 | info->attrs[NL80211_ATTR_MESH_ID]) { | ||
404 | params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); | ||
405 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); | ||
406 | } | ||
407 | |||
290 | rtnl_lock(); | 408 | rtnl_lock(); |
291 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type); | 409 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
410 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | ||
411 | &flags); | ||
412 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, | ||
413 | type, err ? NULL : &flags, ¶ms); | ||
292 | rtnl_unlock(); | 414 | rtnl_unlock(); |
293 | 415 | ||
294 | unlock: | 416 | unlock: |
@@ -299,8 +421,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
299 | static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | 421 | static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) |
300 | { | 422 | { |
301 | struct cfg80211_registered_device *drv; | 423 | struct cfg80211_registered_device *drv; |
424 | struct vif_params params; | ||
302 | int err; | 425 | int err; |
303 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; | 426 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; |
427 | u32 flags; | ||
428 | |||
429 | memset(¶ms, 0, sizeof(params)); | ||
304 | 430 | ||
305 | if (!info->attrs[NL80211_ATTR_IFNAME]) | 431 | if (!info->attrs[NL80211_ATTR_IFNAME]) |
306 | return -EINVAL; | 432 | return -EINVAL; |
@@ -320,11 +446,22 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
320 | goto unlock; | 446 | goto unlock; |
321 | } | 447 | } |
322 | 448 | ||
449 | if (type == NL80211_IFTYPE_MESH_POINT && | ||
450 | info->attrs[NL80211_ATTR_MESH_ID]) { | ||
451 | params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); | ||
452 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); | ||
453 | } | ||
454 | |||
323 | rtnl_lock(); | 455 | rtnl_lock(); |
456 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | ||
457 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | ||
458 | &flags); | ||
324 | err = drv->ops->add_virtual_intf(&drv->wiphy, | 459 | err = drv->ops->add_virtual_intf(&drv->wiphy, |
325 | nla_data(info->attrs[NL80211_ATTR_IFNAME]), type); | 460 | nla_data(info->attrs[NL80211_ATTR_IFNAME]), |
461 | type, err ? NULL : &flags, ¶ms); | ||
326 | rtnl_unlock(); | 462 | rtnl_unlock(); |
327 | 463 | ||
464 | |||
328 | unlock: | 465 | unlock: |
329 | cfg80211_put_dev(drv); | 466 | cfg80211_put_dev(drv); |
330 | return err; | 467 | return err; |
@@ -752,10 +889,10 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags) | |||
752 | 889 | ||
753 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 890 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
754 | int flags, struct net_device *dev, | 891 | int flags, struct net_device *dev, |
755 | u8 *mac_addr, struct station_stats *stats) | 892 | u8 *mac_addr, struct station_info *sinfo) |
756 | { | 893 | { |
757 | void *hdr; | 894 | void *hdr; |
758 | struct nlattr *statsattr; | 895 | struct nlattr *sinfoattr; |
759 | 896 | ||
760 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | 897 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); |
761 | if (!hdr) | 898 | if (!hdr) |
@@ -764,20 +901,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
764 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | 901 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); |
765 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | 902 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); |
766 | 903 | ||
767 | statsattr = nla_nest_start(msg, NL80211_ATTR_STA_STATS); | 904 | sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); |
768 | if (!statsattr) | 905 | if (!sinfoattr) |
769 | goto nla_put_failure; | 906 | goto nla_put_failure; |
770 | if (stats->filled & STATION_STAT_INACTIVE_TIME) | 907 | if (sinfo->filled & STATION_INFO_INACTIVE_TIME) |
771 | NLA_PUT_U32(msg, NL80211_STA_STAT_INACTIVE_TIME, | 908 | NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, |
772 | stats->inactive_time); | 909 | sinfo->inactive_time); |
773 | if (stats->filled & STATION_STAT_RX_BYTES) | 910 | if (sinfo->filled & STATION_INFO_RX_BYTES) |
774 | NLA_PUT_U32(msg, NL80211_STA_STAT_RX_BYTES, | 911 | NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES, |
775 | stats->rx_bytes); | 912 | sinfo->rx_bytes); |
776 | if (stats->filled & STATION_STAT_TX_BYTES) | 913 | if (sinfo->filled & STATION_INFO_TX_BYTES) |
777 | NLA_PUT_U32(msg, NL80211_STA_STAT_TX_BYTES, | 914 | NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES, |
778 | stats->tx_bytes); | 915 | sinfo->tx_bytes); |
779 | 916 | if (sinfo->filled & STATION_INFO_LLID) | |
780 | nla_nest_end(msg, statsattr); | 917 | NLA_PUT_U16(msg, NL80211_STA_INFO_LLID, |
918 | sinfo->llid); | ||
919 | if (sinfo->filled & STATION_INFO_PLID) | ||
920 | NLA_PUT_U16(msg, NL80211_STA_INFO_PLID, | ||
921 | sinfo->plid); | ||
922 | if (sinfo->filled & STATION_INFO_PLINK_STATE) | ||
923 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, | ||
924 | sinfo->plink_state); | ||
925 | |||
926 | nla_nest_end(msg, sinfoattr); | ||
781 | 927 | ||
782 | return genlmsg_end(msg, hdr); | 928 | return genlmsg_end(msg, hdr); |
783 | 929 | ||
@@ -785,17 +931,80 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
785 | return genlmsg_cancel(msg, hdr); | 931 | return genlmsg_cancel(msg, hdr); |
786 | } | 932 | } |
787 | 933 | ||
934 | static int nl80211_dump_station(struct sk_buff *skb, | ||
935 | struct netlink_callback *cb) | ||
936 | { | ||
937 | int wp_idx = 0; | ||
938 | int if_idx = 0; | ||
939 | int sta_idx = cb->args[2]; | ||
940 | int wp_start = cb->args[0]; | ||
941 | int if_start = cb->args[1]; | ||
942 | struct station_info sinfo; | ||
943 | struct cfg80211_registered_device *dev; | ||
944 | struct wireless_dev *wdev; | ||
945 | u8 mac_addr[ETH_ALEN]; | ||
946 | int err; | ||
947 | int exit = 0; | ||
948 | |||
949 | /* TODO: filter by device */ | ||
950 | mutex_lock(&cfg80211_drv_mutex); | ||
951 | list_for_each_entry(dev, &cfg80211_drv_list, list) { | ||
952 | if (exit) | ||
953 | break; | ||
954 | if (++wp_idx < wp_start) | ||
955 | continue; | ||
956 | if_idx = 0; | ||
957 | |||
958 | mutex_lock(&dev->devlist_mtx); | ||
959 | list_for_each_entry(wdev, &dev->netdev_list, list) { | ||
960 | if (exit) | ||
961 | break; | ||
962 | if (++if_idx < if_start) | ||
963 | continue; | ||
964 | if (!dev->ops->dump_station) | ||
965 | continue; | ||
966 | |||
967 | for (;; ++sta_idx) { | ||
968 | rtnl_lock(); | ||
969 | err = dev->ops->dump_station(&dev->wiphy, | ||
970 | wdev->netdev, sta_idx, mac_addr, | ||
971 | &sinfo); | ||
972 | rtnl_unlock(); | ||
973 | if (err) { | ||
974 | sta_idx = 0; | ||
975 | break; | ||
976 | } | ||
977 | if (nl80211_send_station(skb, | ||
978 | NETLINK_CB(cb->skb).pid, | ||
979 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
980 | wdev->netdev, mac_addr, | ||
981 | &sinfo) < 0) { | ||
982 | exit = 1; | ||
983 | break; | ||
984 | } | ||
985 | } | ||
986 | } | ||
987 | mutex_unlock(&dev->devlist_mtx); | ||
988 | } | ||
989 | mutex_unlock(&cfg80211_drv_mutex); | ||
990 | |||
991 | cb->args[0] = wp_idx; | ||
992 | cb->args[1] = if_idx; | ||
993 | cb->args[2] = sta_idx; | ||
994 | |||
995 | return skb->len; | ||
996 | } | ||
788 | 997 | ||
789 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | 998 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) |
790 | { | 999 | { |
791 | struct cfg80211_registered_device *drv; | 1000 | struct cfg80211_registered_device *drv; |
792 | int err; | 1001 | int err; |
793 | struct net_device *dev; | 1002 | struct net_device *dev; |
794 | struct station_stats stats; | 1003 | struct station_info sinfo; |
795 | struct sk_buff *msg; | 1004 | struct sk_buff *msg; |
796 | u8 *mac_addr = NULL; | 1005 | u8 *mac_addr = NULL; |
797 | 1006 | ||
798 | memset(&stats, 0, sizeof(stats)); | 1007 | memset(&sinfo, 0, sizeof(sinfo)); |
799 | 1008 | ||
800 | if (!info->attrs[NL80211_ATTR_MAC]) | 1009 | if (!info->attrs[NL80211_ATTR_MAC]) |
801 | return -EINVAL; | 1010 | return -EINVAL; |
@@ -812,15 +1021,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | |||
812 | } | 1021 | } |
813 | 1022 | ||
814 | rtnl_lock(); | 1023 | rtnl_lock(); |
815 | err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &stats); | 1024 | err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); |
816 | rtnl_unlock(); | 1025 | rtnl_unlock(); |
817 | 1026 | ||
1027 | if (err) | ||
1028 | goto out; | ||
1029 | |||
818 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | 1030 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); |
819 | if (!msg) | 1031 | if (!msg) |
820 | goto out; | 1032 | goto out; |
821 | 1033 | ||
822 | if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, | 1034 | if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, |
823 | dev, mac_addr, &stats) < 0) | 1035 | dev, mac_addr, &sinfo) < 0) |
824 | goto out_free; | 1036 | goto out_free; |
825 | 1037 | ||
826 | err = genlmsg_unicast(msg, info->snd_pid); | 1038 | err = genlmsg_unicast(msg, info->snd_pid); |
@@ -891,6 +1103,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
891 | ¶ms.station_flags)) | 1103 | ¶ms.station_flags)) |
892 | return -EINVAL; | 1104 | return -EINVAL; |
893 | 1105 | ||
1106 | if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) | ||
1107 | params.plink_action = | ||
1108 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); | ||
1109 | |||
894 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | 1110 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); |
895 | if (err) | 1111 | if (err) |
896 | return err; | 1112 | return err; |
@@ -1005,6 +1221,273 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
1005 | return err; | 1221 | return err; |
1006 | } | 1222 | } |
1007 | 1223 | ||
1224 | static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, | ||
1225 | int flags, struct net_device *dev, | ||
1226 | u8 *dst, u8 *next_hop, | ||
1227 | struct mpath_info *pinfo) | ||
1228 | { | ||
1229 | void *hdr; | ||
1230 | struct nlattr *pinfoattr; | ||
1231 | |||
1232 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | ||
1233 | if (!hdr) | ||
1234 | return -1; | ||
1235 | |||
1236 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
1237 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); | ||
1238 | NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); | ||
1239 | |||
1240 | pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); | ||
1241 | if (!pinfoattr) | ||
1242 | goto nla_put_failure; | ||
1243 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) | ||
1244 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, | ||
1245 | pinfo->frame_qlen); | ||
1246 | if (pinfo->filled & MPATH_INFO_DSN) | ||
1247 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN, | ||
1248 | pinfo->dsn); | ||
1249 | if (pinfo->filled & MPATH_INFO_METRIC) | ||
1250 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, | ||
1251 | pinfo->metric); | ||
1252 | if (pinfo->filled & MPATH_INFO_EXPTIME) | ||
1253 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME, | ||
1254 | pinfo->exptime); | ||
1255 | if (pinfo->filled & MPATH_INFO_FLAGS) | ||
1256 | NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS, | ||
1257 | pinfo->flags); | ||
1258 | if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) | ||
1259 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, | ||
1260 | pinfo->discovery_timeout); | ||
1261 | if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) | ||
1262 | NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, | ||
1263 | pinfo->discovery_retries); | ||
1264 | |||
1265 | nla_nest_end(msg, pinfoattr); | ||
1266 | |||
1267 | return genlmsg_end(msg, hdr); | ||
1268 | |||
1269 | nla_put_failure: | ||
1270 | return genlmsg_cancel(msg, hdr); | ||
1271 | } | ||
1272 | |||
1273 | static int nl80211_dump_mpath(struct sk_buff *skb, | ||
1274 | struct netlink_callback *cb) | ||
1275 | { | ||
1276 | int wp_idx = 0; | ||
1277 | int if_idx = 0; | ||
1278 | int sta_idx = cb->args[2]; | ||
1279 | int wp_start = cb->args[0]; | ||
1280 | int if_start = cb->args[1]; | ||
1281 | struct mpath_info pinfo; | ||
1282 | struct cfg80211_registered_device *dev; | ||
1283 | struct wireless_dev *wdev; | ||
1284 | u8 dst[ETH_ALEN]; | ||
1285 | u8 next_hop[ETH_ALEN]; | ||
1286 | int err; | ||
1287 | int exit = 0; | ||
1288 | |||
1289 | /* TODO: filter by device */ | ||
1290 | mutex_lock(&cfg80211_drv_mutex); | ||
1291 | list_for_each_entry(dev, &cfg80211_drv_list, list) { | ||
1292 | if (exit) | ||
1293 | break; | ||
1294 | if (++wp_idx < wp_start) | ||
1295 | continue; | ||
1296 | if_idx = 0; | ||
1297 | |||
1298 | mutex_lock(&dev->devlist_mtx); | ||
1299 | list_for_each_entry(wdev, &dev->netdev_list, list) { | ||
1300 | if (exit) | ||
1301 | break; | ||
1302 | if (++if_idx < if_start) | ||
1303 | continue; | ||
1304 | if (!dev->ops->dump_mpath) | ||
1305 | continue; | ||
1306 | |||
1307 | for (;; ++sta_idx) { | ||
1308 | rtnl_lock(); | ||
1309 | err = dev->ops->dump_mpath(&dev->wiphy, | ||
1310 | wdev->netdev, sta_idx, dst, | ||
1311 | next_hop, &pinfo); | ||
1312 | rtnl_unlock(); | ||
1313 | if (err) { | ||
1314 | sta_idx = 0; | ||
1315 | break; | ||
1316 | } | ||
1317 | if (nl80211_send_mpath(skb, | ||
1318 | NETLINK_CB(cb->skb).pid, | ||
1319 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
1320 | wdev->netdev, dst, next_hop, | ||
1321 | &pinfo) < 0) { | ||
1322 | exit = 1; | ||
1323 | break; | ||
1324 | } | ||
1325 | } | ||
1326 | } | ||
1327 | mutex_unlock(&dev->devlist_mtx); | ||
1328 | } | ||
1329 | mutex_unlock(&cfg80211_drv_mutex); | ||
1330 | |||
1331 | cb->args[0] = wp_idx; | ||
1332 | cb->args[1] = if_idx; | ||
1333 | cb->args[2] = sta_idx; | ||
1334 | |||
1335 | return skb->len; | ||
1336 | } | ||
1337 | |||
1338 | static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1339 | { | ||
1340 | struct cfg80211_registered_device *drv; | ||
1341 | int err; | ||
1342 | struct net_device *dev; | ||
1343 | struct mpath_info pinfo; | ||
1344 | struct sk_buff *msg; | ||
1345 | u8 *dst = NULL; | ||
1346 | u8 next_hop[ETH_ALEN]; | ||
1347 | |||
1348 | memset(&pinfo, 0, sizeof(pinfo)); | ||
1349 | |||
1350 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
1351 | return -EINVAL; | ||
1352 | |||
1353 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1354 | |||
1355 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1356 | if (err) | ||
1357 | return err; | ||
1358 | |||
1359 | if (!drv->ops->get_mpath) { | ||
1360 | err = -EOPNOTSUPP; | ||
1361 | goto out; | ||
1362 | } | ||
1363 | |||
1364 | rtnl_lock(); | ||
1365 | err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); | ||
1366 | rtnl_unlock(); | ||
1367 | |||
1368 | if (err) | ||
1369 | goto out; | ||
1370 | |||
1371 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
1372 | if (!msg) | ||
1373 | goto out; | ||
1374 | |||
1375 | if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0, | ||
1376 | dev, dst, next_hop, &pinfo) < 0) | ||
1377 | goto out_free; | ||
1378 | |||
1379 | err = genlmsg_unicast(msg, info->snd_pid); | ||
1380 | goto out; | ||
1381 | |||
1382 | out_free: | ||
1383 | nlmsg_free(msg); | ||
1384 | |||
1385 | out: | ||
1386 | cfg80211_put_dev(drv); | ||
1387 | dev_put(dev); | ||
1388 | return err; | ||
1389 | } | ||
1390 | |||
1391 | static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1392 | { | ||
1393 | struct cfg80211_registered_device *drv; | ||
1394 | int err; | ||
1395 | struct net_device *dev; | ||
1396 | u8 *dst = NULL; | ||
1397 | u8 *next_hop = NULL; | ||
1398 | |||
1399 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
1400 | return -EINVAL; | ||
1401 | |||
1402 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) | ||
1403 | return -EINVAL; | ||
1404 | |||
1405 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1406 | next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); | ||
1407 | |||
1408 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1409 | if (err) | ||
1410 | return err; | ||
1411 | |||
1412 | if (!drv->ops->change_mpath) { | ||
1413 | err = -EOPNOTSUPP; | ||
1414 | goto out; | ||
1415 | } | ||
1416 | |||
1417 | rtnl_lock(); | ||
1418 | err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); | ||
1419 | rtnl_unlock(); | ||
1420 | |||
1421 | out: | ||
1422 | cfg80211_put_dev(drv); | ||
1423 | dev_put(dev); | ||
1424 | return err; | ||
1425 | } | ||
1426 | static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1427 | { | ||
1428 | struct cfg80211_registered_device *drv; | ||
1429 | int err; | ||
1430 | struct net_device *dev; | ||
1431 | u8 *dst = NULL; | ||
1432 | u8 *next_hop = NULL; | ||
1433 | |||
1434 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
1435 | return -EINVAL; | ||
1436 | |||
1437 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) | ||
1438 | return -EINVAL; | ||
1439 | |||
1440 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1441 | next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); | ||
1442 | |||
1443 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1444 | if (err) | ||
1445 | return err; | ||
1446 | |||
1447 | if (!drv->ops->add_mpath) { | ||
1448 | err = -EOPNOTSUPP; | ||
1449 | goto out; | ||
1450 | } | ||
1451 | |||
1452 | rtnl_lock(); | ||
1453 | err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); | ||
1454 | rtnl_unlock(); | ||
1455 | |||
1456 | out: | ||
1457 | cfg80211_put_dev(drv); | ||
1458 | dev_put(dev); | ||
1459 | return err; | ||
1460 | } | ||
1461 | |||
1462 | static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1463 | { | ||
1464 | struct cfg80211_registered_device *drv; | ||
1465 | int err; | ||
1466 | struct net_device *dev; | ||
1467 | u8 *dst = NULL; | ||
1468 | |||
1469 | if (info->attrs[NL80211_ATTR_MAC]) | ||
1470 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1471 | |||
1472 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1473 | if (err) | ||
1474 | return err; | ||
1475 | |||
1476 | if (!drv->ops->del_mpath) { | ||
1477 | err = -EOPNOTSUPP; | ||
1478 | goto out; | ||
1479 | } | ||
1480 | |||
1481 | rtnl_lock(); | ||
1482 | err = drv->ops->del_mpath(&drv->wiphy, dev, dst); | ||
1483 | rtnl_unlock(); | ||
1484 | |||
1485 | out: | ||
1486 | cfg80211_put_dev(drv); | ||
1487 | dev_put(dev); | ||
1488 | return err; | ||
1489 | } | ||
1490 | |||
1008 | static struct genl_ops nl80211_ops[] = { | 1491 | static struct genl_ops nl80211_ops[] = { |
1009 | { | 1492 | { |
1010 | .cmd = NL80211_CMD_GET_WIPHY, | 1493 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -1089,7 +1572,7 @@ static struct genl_ops nl80211_ops[] = { | |||
1089 | { | 1572 | { |
1090 | .cmd = NL80211_CMD_GET_STATION, | 1573 | .cmd = NL80211_CMD_GET_STATION, |
1091 | .doit = nl80211_get_station, | 1574 | .doit = nl80211_get_station, |
1092 | /* TODO: implement dumpit */ | 1575 | .dumpit = nl80211_dump_station, |
1093 | .policy = nl80211_policy, | 1576 | .policy = nl80211_policy, |
1094 | .flags = GENL_ADMIN_PERM, | 1577 | .flags = GENL_ADMIN_PERM, |
1095 | }, | 1578 | }, |
@@ -1111,6 +1594,31 @@ static struct genl_ops nl80211_ops[] = { | |||
1111 | .policy = nl80211_policy, | 1594 | .policy = nl80211_policy, |
1112 | .flags = GENL_ADMIN_PERM, | 1595 | .flags = GENL_ADMIN_PERM, |
1113 | }, | 1596 | }, |
1597 | { | ||
1598 | .cmd = NL80211_CMD_GET_MPATH, | ||
1599 | .doit = nl80211_get_mpath, | ||
1600 | .dumpit = nl80211_dump_mpath, | ||
1601 | .policy = nl80211_policy, | ||
1602 | .flags = GENL_ADMIN_PERM, | ||
1603 | }, | ||
1604 | { | ||
1605 | .cmd = NL80211_CMD_SET_MPATH, | ||
1606 | .doit = nl80211_set_mpath, | ||
1607 | .policy = nl80211_policy, | ||
1608 | .flags = GENL_ADMIN_PERM, | ||
1609 | }, | ||
1610 | { | ||
1611 | .cmd = NL80211_CMD_NEW_MPATH, | ||
1612 | .doit = nl80211_new_mpath, | ||
1613 | .policy = nl80211_policy, | ||
1614 | .flags = GENL_ADMIN_PERM, | ||
1615 | }, | ||
1616 | { | ||
1617 | .cmd = NL80211_CMD_DEL_MPATH, | ||
1618 | .doit = nl80211_del_mpath, | ||
1619 | .policy = nl80211_policy, | ||
1620 | .flags = GENL_ADMIN_PERM, | ||
1621 | }, | ||
1114 | }; | 1622 | }; |
1115 | 1623 | ||
1116 | /* multicast groups */ | 1624 | /* multicast groups */ |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c new file mode 100644 index 000000000000..185488da2466 --- /dev/null +++ b/net/wireless/reg.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * Copyright 2002-2005, Instant802 Networks, Inc. | ||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | ||
4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * This regulatory domain control implementation is highly incomplete, it | ||
13 | * only exists for the purpose of not regressing mac80211. | ||
14 | * | ||
15 | * For now, drivers can restrict the set of allowed channels by either | ||
16 | * not registering those channels or setting the IEEE80211_CHAN_DISABLED | ||
17 | * flag; that flag will only be *set* by this code, never *cleared. | ||
18 | * | ||
19 | * The usual implementation is for a driver to read a device EEPROM to | ||
20 | * determine which regulatory domain it should be operating under, then | ||
21 | * looking up the allowable channels in a driver-local table and finally | ||
22 | * registering those channels in the wiphy structure. | ||
23 | * | ||
24 | * Alternatively, drivers that trust the regulatory domain control here | ||
25 | * will register a complete set of capabilities and the control code | ||
26 | * will restrict the set by setting the IEEE80211_CHAN_* flags. | ||
27 | */ | ||
28 | #include <linux/kernel.h> | ||
29 | #include <net/wireless.h> | ||
30 | #include "core.h" | ||
31 | |||
32 | static char *ieee80211_regdom = "US"; | ||
33 | module_param(ieee80211_regdom, charp, 0444); | ||
34 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | ||
35 | |||
36 | struct ieee80211_channel_range { | ||
37 | short start_freq; | ||
38 | short end_freq; | ||
39 | int max_power; | ||
40 | int max_antenna_gain; | ||
41 | u32 flags; | ||
42 | }; | ||
43 | |||
44 | struct ieee80211_regdomain { | ||
45 | const char *code; | ||
46 | const struct ieee80211_channel_range *ranges; | ||
47 | int n_ranges; | ||
48 | }; | ||
49 | |||
50 | #define RANGE_PWR(_start, _end, _pwr, _ag, _flags) \ | ||
51 | { _start, _end, _pwr, _ag, _flags } | ||
52 | |||
53 | |||
54 | /* | ||
55 | * Ideally, in the future, these definitions will be loaded from a | ||
56 | * userspace table via some daemon. | ||
57 | */ | ||
58 | static const struct ieee80211_channel_range ieee80211_US_channels[] = { | ||
59 | /* IEEE 802.11b/g, channels 1..11 */ | ||
60 | RANGE_PWR(2412, 2462, 27, 6, 0), | ||
61 | /* IEEE 802.11a, channel 36*/ | ||
62 | RANGE_PWR(5180, 5180, 23, 6, 0), | ||
63 | /* IEEE 802.11a, channel 40*/ | ||
64 | RANGE_PWR(5200, 5200, 23, 6, 0), | ||
65 | /* IEEE 802.11a, channel 44*/ | ||
66 | RANGE_PWR(5220, 5220, 23, 6, 0), | ||
67 | /* IEEE 802.11a, channels 48..64 */ | ||
68 | RANGE_PWR(5240, 5320, 23, 6, 0), | ||
69 | /* IEEE 802.11a, channels 149..165, outdoor */ | ||
70 | RANGE_PWR(5745, 5825, 30, 6, 0), | ||
71 | }; | ||
72 | |||
73 | static const struct ieee80211_channel_range ieee80211_JP_channels[] = { | ||
74 | /* IEEE 802.11b/g, channels 1..14 */ | ||
75 | RANGE_PWR(2412, 2484, 20, 6, 0), | ||
76 | /* IEEE 802.11a, channels 34..48 */ | ||
77 | RANGE_PWR(5170, 5240, 20, 6, IEEE80211_CHAN_PASSIVE_SCAN), | ||
78 | /* IEEE 802.11a, channels 52..64 */ | ||
79 | RANGE_PWR(5260, 5320, 20, 6, IEEE80211_CHAN_NO_IBSS | | ||
80 | IEEE80211_CHAN_RADAR), | ||
81 | }; | ||
82 | |||
83 | #define REGDOM(_code) \ | ||
84 | { \ | ||
85 | .code = __stringify(_code), \ | ||
86 | .ranges = ieee80211_ ##_code## _channels, \ | ||
87 | .n_ranges = ARRAY_SIZE(ieee80211_ ##_code## _channels), \ | ||
88 | } | ||
89 | |||
90 | static const struct ieee80211_regdomain ieee80211_regdoms[] = { | ||
91 | REGDOM(US), | ||
92 | REGDOM(JP), | ||
93 | }; | ||
94 | |||
95 | |||
96 | static const struct ieee80211_regdomain *get_regdom(void) | ||
97 | { | ||
98 | static const struct ieee80211_channel_range | ||
99 | ieee80211_world_channels[] = { | ||
100 | /* IEEE 802.11b/g, channels 1..11 */ | ||
101 | RANGE_PWR(2412, 2462, 27, 6, 0), | ||
102 | }; | ||
103 | static const struct ieee80211_regdomain regdom_world = REGDOM(world); | ||
104 | int i; | ||
105 | |||
106 | for (i = 0; i < ARRAY_SIZE(ieee80211_regdoms); i++) | ||
107 | if (strcmp(ieee80211_regdom, ieee80211_regdoms[i].code) == 0) | ||
108 | return &ieee80211_regdoms[i]; | ||
109 | |||
110 | return ®dom_world; | ||
111 | } | ||
112 | |||
113 | |||
114 | static void handle_channel(struct ieee80211_channel *chan, | ||
115 | const struct ieee80211_regdomain *rd) | ||
116 | { | ||
117 | int i; | ||
118 | u32 flags = chan->orig_flags; | ||
119 | const struct ieee80211_channel_range *rg = NULL; | ||
120 | |||
121 | for (i = 0; i < rd->n_ranges; i++) { | ||
122 | if (rd->ranges[i].start_freq <= chan->center_freq && | ||
123 | chan->center_freq <= rd->ranges[i].end_freq) { | ||
124 | rg = &rd->ranges[i]; | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | if (!rg) { | ||
130 | /* not found */ | ||
131 | flags |= IEEE80211_CHAN_DISABLED; | ||
132 | chan->flags = flags; | ||
133 | return; | ||
134 | } | ||
135 | |||
136 | chan->flags = flags; | ||
137 | chan->max_antenna_gain = min(chan->orig_mag, | ||
138 | rg->max_antenna_gain); | ||
139 | if (chan->orig_mpwr) | ||
140 | chan->max_power = min(chan->orig_mpwr, rg->max_power); | ||
141 | else | ||
142 | chan->max_power = rg->max_power; | ||
143 | } | ||
144 | |||
145 | static void handle_band(struct ieee80211_supported_band *sband, | ||
146 | const struct ieee80211_regdomain *rd) | ||
147 | { | ||
148 | int i; | ||
149 | |||
150 | for (i = 0; i < sband->n_channels; i++) | ||
151 | handle_channel(&sband->channels[i], rd); | ||
152 | } | ||
153 | |||
154 | void wiphy_update_regulatory(struct wiphy *wiphy) | ||
155 | { | ||
156 | enum ieee80211_band band; | ||
157 | const struct ieee80211_regdomain *rd = get_regdom(); | ||
158 | |||
159 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
160 | if (wiphy->bands[band]) | ||
161 | handle_band(wiphy->bands[band], rd); | ||
162 | } | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c new file mode 100644 index 000000000000..f54424693a38 --- /dev/null +++ b/net/wireless/util.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * Wireless utility functions | ||
3 | * | ||
4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | ||
5 | */ | ||
6 | #include <net/wireless.h> | ||
7 | #include <asm/bitops.h> | ||
8 | #include "core.h" | ||
9 | |||
10 | int ieee80211_channel_to_frequency(int chan) | ||
11 | { | ||
12 | if (chan < 14) | ||
13 | return 2407 + chan * 5; | ||
14 | |||
15 | if (chan == 14) | ||
16 | return 2484; | ||
17 | |||
18 | /* FIXME: 802.11j 17.3.8.3.2 */ | ||
19 | return (chan + 1000) * 5; | ||
20 | } | ||
21 | EXPORT_SYMBOL(ieee80211_channel_to_frequency); | ||
22 | |||
23 | int ieee80211_frequency_to_channel(int freq) | ||
24 | { | ||
25 | if (freq == 2484) | ||
26 | return 14; | ||
27 | |||
28 | if (freq < 2484) | ||
29 | return (freq - 2407) / 5; | ||
30 | |||
31 | /* FIXME: 802.11j 17.3.8.3.2 */ | ||
32 | return freq/5 - 1000; | ||
33 | } | ||
34 | EXPORT_SYMBOL(ieee80211_frequency_to_channel); | ||
35 | |||
36 | struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, | ||
37 | int freq) | ||
38 | { | ||
39 | enum ieee80211_band band; | ||
40 | struct ieee80211_supported_band *sband; | ||
41 | int i; | ||
42 | |||
43 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
44 | sband = wiphy->bands[band]; | ||
45 | |||
46 | if (!sband) | ||
47 | continue; | ||
48 | |||
49 | for (i = 0; i < sband->n_channels; i++) { | ||
50 | if (sband->channels[i].center_freq == freq) | ||
51 | return &sband->channels[i]; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | return NULL; | ||
56 | } | ||
57 | EXPORT_SYMBOL(__ieee80211_get_channel); | ||
58 | |||
59 | static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, | ||
60 | enum ieee80211_band band) | ||
61 | { | ||
62 | int i, want; | ||
63 | |||
64 | switch (band) { | ||
65 | case IEEE80211_BAND_5GHZ: | ||
66 | want = 3; | ||
67 | for (i = 0; i < sband->n_bitrates; i++) { | ||
68 | if (sband->bitrates[i].bitrate == 60 || | ||
69 | sband->bitrates[i].bitrate == 120 || | ||
70 | sband->bitrates[i].bitrate == 240) { | ||
71 | sband->bitrates[i].flags |= | ||
72 | IEEE80211_RATE_MANDATORY_A; | ||
73 | want--; | ||
74 | } | ||
75 | } | ||
76 | WARN_ON(want); | ||
77 | break; | ||
78 | case IEEE80211_BAND_2GHZ: | ||
79 | want = 7; | ||
80 | for (i = 0; i < sband->n_bitrates; i++) { | ||
81 | if (sband->bitrates[i].bitrate == 10) { | ||
82 | sband->bitrates[i].flags |= | ||
83 | IEEE80211_RATE_MANDATORY_B | | ||
84 | IEEE80211_RATE_MANDATORY_G; | ||
85 | want--; | ||
86 | } | ||
87 | |||
88 | if (sband->bitrates[i].bitrate == 20 || | ||
89 | sband->bitrates[i].bitrate == 55 || | ||
90 | sband->bitrates[i].bitrate == 110 || | ||
91 | sband->bitrates[i].bitrate == 60 || | ||
92 | sband->bitrates[i].bitrate == 120 || | ||
93 | sband->bitrates[i].bitrate == 240) { | ||
94 | sband->bitrates[i].flags |= | ||
95 | IEEE80211_RATE_MANDATORY_G; | ||
96 | want--; | ||
97 | } | ||
98 | |||
99 | if (sband->bitrates[i].bitrate != 10 && | ||
100 | sband->bitrates[i].bitrate != 20 && | ||
101 | sband->bitrates[i].bitrate != 55 && | ||
102 | sband->bitrates[i].bitrate != 110) | ||
103 | sband->bitrates[i].flags |= | ||
104 | IEEE80211_RATE_ERP_G; | ||
105 | } | ||
106 | WARN_ON(want != 0 && want != 3 && want != 6); | ||
107 | break; | ||
108 | case IEEE80211_NUM_BANDS: | ||
109 | WARN_ON(1); | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | void ieee80211_set_bitrate_flags(struct wiphy *wiphy) | ||
115 | { | ||
116 | enum ieee80211_band band; | ||
117 | |||
118 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
119 | if (wiphy->bands[band]) | ||
120 | set_mandatory_flags_band(wiphy->bands[band], band); | ||
121 | } | ||
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 2c569b63e7d8..947188a5b937 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -1157,7 +1157,7 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | |||
1157 | struct sk_buff *skb; | 1157 | struct sk_buff *skb; |
1158 | int err; | 1158 | int err; |
1159 | 1159 | ||
1160 | if (dev->nd_net != &init_net) | 1160 | if (dev_net(dev) != &init_net) |
1161 | return; | 1161 | return; |
1162 | 1162 | ||
1163 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | 1163 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |