diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 85 |
1 files changed, 70 insertions, 15 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4335f76be71f..1e728fff474e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -60,7 +60,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
60 | .len = BUS_ID_SIZE-1 }, | 60 | .len = BUS_ID_SIZE-1 }, |
61 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, | 61 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, |
62 | [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, | 62 | [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET] = { .type = NLA_U32 }, | 63 | [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, |
64 | 64 | ||
65 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 65 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
66 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 66 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -362,8 +362,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
362 | } | 362 | } |
363 | 363 | ||
364 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 364 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
365 | enum nl80211_sec_chan_offset sec_chan_offset = | 365 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; |
366 | NL80211_SEC_CHAN_NO_HT; | ||
367 | struct ieee80211_channel *chan; | 366 | struct ieee80211_channel *chan; |
368 | struct ieee80211_sta_ht_cap *ht_cap; | 367 | struct ieee80211_sta_ht_cap *ht_cap; |
369 | u32 freq, sec_freq; | 368 | u32 freq, sec_freq; |
@@ -375,13 +374,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
375 | 374 | ||
376 | result = -EINVAL; | 375 | result = -EINVAL; |
377 | 376 | ||
378 | if (info->attrs[NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]) { | 377 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { |
379 | sec_chan_offset = nla_get_u32(info->attrs[ | 378 | channel_type = nla_get_u32(info->attrs[ |
380 | NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]); | 379 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); |
381 | if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT && | 380 | if (channel_type != NL80211_CHAN_NO_HT && |
382 | sec_chan_offset != NL80211_SEC_CHAN_DISABLED && | 381 | channel_type != NL80211_CHAN_HT20 && |
383 | sec_chan_offset != NL80211_SEC_CHAN_BELOW && | 382 | channel_type != NL80211_CHAN_HT40PLUS && |
384 | sec_chan_offset != NL80211_SEC_CHAN_ABOVE) | 383 | channel_type != NL80211_CHAN_HT40MINUS) |
385 | goto bad_res; | 384 | goto bad_res; |
386 | } | 385 | } |
387 | 386 | ||
@@ -392,9 +391,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
392 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | 391 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
393 | goto bad_res; | 392 | goto bad_res; |
394 | 393 | ||
395 | if (sec_chan_offset == NL80211_SEC_CHAN_BELOW) | 394 | if (channel_type == NL80211_CHAN_HT40MINUS) |
396 | sec_freq = freq - 20; | 395 | sec_freq = freq - 20; |
397 | else if (sec_chan_offset == NL80211_SEC_CHAN_ABOVE) | 396 | else if (channel_type == NL80211_CHAN_HT40PLUS) |
398 | sec_freq = freq + 20; | 397 | sec_freq = freq + 20; |
399 | else | 398 | else |
400 | sec_freq = 0; | 399 | sec_freq = 0; |
@@ -402,7 +401,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
402 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | 401 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; |
403 | 402 | ||
404 | /* no HT capabilities */ | 403 | /* no HT capabilities */ |
405 | if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT && | 404 | if (channel_type != NL80211_CHAN_NO_HT && |
406 | !ht_cap->ht_supported) | 405 | !ht_cap->ht_supported) |
407 | goto bad_res; | 406 | goto bad_res; |
408 | 407 | ||
@@ -422,7 +421,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
422 | } | 421 | } |
423 | 422 | ||
424 | result = rdev->ops->set_channel(&rdev->wiphy, chan, | 423 | result = rdev->ops->set_channel(&rdev->wiphy, chan, |
425 | sec_chan_offset); | 424 | channel_type); |
426 | if (result) | 425 | if (result) |
427 | goto bad_res; | 426 | goto bad_res; |
428 | } | 427 | } |
@@ -1091,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags) | |||
1091 | return 0; | 1090 | return 0; |
1092 | } | 1091 | } |
1093 | 1092 | ||
1093 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
1094 | { | ||
1095 | int modulation, streams, bitrate; | ||
1096 | |||
1097 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
1098 | return rate->legacy; | ||
1099 | |||
1100 | /* the formula below does only work for MCS values smaller than 32 */ | ||
1101 | if (rate->mcs >= 32) | ||
1102 | return 0; | ||
1103 | |||
1104 | modulation = rate->mcs & 7; | ||
1105 | streams = (rate->mcs >> 3) + 1; | ||
1106 | |||
1107 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
1108 | 13500000 : 6500000; | ||
1109 | |||
1110 | if (modulation < 4) | ||
1111 | bitrate *= (modulation + 1); | ||
1112 | else if (modulation == 4) | ||
1113 | bitrate *= (modulation + 2); | ||
1114 | else | ||
1115 | bitrate *= (modulation + 3); | ||
1116 | |||
1117 | bitrate *= streams; | ||
1118 | |||
1119 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1120 | bitrate = (bitrate / 9) * 10; | ||
1121 | |||
1122 | /* do NOT round down here */ | ||
1123 | return (bitrate + 50000) / 100000; | ||
1124 | } | ||
1125 | |||
1094 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1126 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
1095 | int flags, struct net_device *dev, | 1127 | int flags, struct net_device *dev, |
1096 | u8 *mac_addr, struct station_info *sinfo) | 1128 | u8 *mac_addr, struct station_info *sinfo) |
1097 | { | 1129 | { |
1098 | void *hdr; | 1130 | void *hdr; |
1099 | struct nlattr *sinfoattr; | 1131 | struct nlattr *sinfoattr, *txrate; |
1132 | u16 bitrate; | ||
1100 | 1133 | ||
1101 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | 1134 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); |
1102 | if (!hdr) | 1135 | if (!hdr) |
@@ -1126,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
1126 | if (sinfo->filled & STATION_INFO_PLINK_STATE) | 1159 | if (sinfo->filled & STATION_INFO_PLINK_STATE) |
1127 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, | 1160 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, |
1128 | sinfo->plink_state); | 1161 | sinfo->plink_state); |
1162 | if (sinfo->filled & STATION_INFO_SIGNAL) | ||
1163 | NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, | ||
1164 | sinfo->signal); | ||
1165 | if (sinfo->filled & STATION_INFO_TX_BITRATE) { | ||
1166 | txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); | ||
1167 | if (!txrate) | ||
1168 | goto nla_put_failure; | ||
1169 | |||
1170 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | ||
1171 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | ||
1172 | if (bitrate > 0) | ||
1173 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | ||
1129 | 1174 | ||
1175 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS) | ||
1176 | NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, | ||
1177 | sinfo->txrate.mcs); | ||
1178 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) | ||
1179 | NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); | ||
1180 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1181 | NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); | ||
1182 | |||
1183 | nla_nest_end(msg, txrate); | ||
1184 | } | ||
1130 | nla_nest_end(msg, sinfoattr); | 1185 | nla_nest_end(msg, sinfoattr); |
1131 | 1186 | ||
1132 | return genlmsg_end(msg, hdr); | 1187 | return genlmsg_end(msg, hdr); |