chan.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. #include <linux/export.h>
  9. #include <net/cfg80211.h>
  10. #include "core.h"
  11. void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
  12. struct ieee80211_channel *chan,
  13. enum nl80211_channel_type chan_type)
  14. {
  15. if (WARN_ON(!chan))
  16. return;
  17. chandef->chan = chan;
  18. chandef->center_freq2 = 0;
  19. switch (chan_type) {
  20. case NL80211_CHAN_NO_HT:
  21. chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
  22. chandef->center_freq1 = chan->center_freq;
  23. break;
  24. case NL80211_CHAN_HT20:
  25. chandef->width = NL80211_CHAN_WIDTH_20;
  26. chandef->center_freq1 = chan->center_freq;
  27. break;
  28. case NL80211_CHAN_HT40PLUS:
  29. chandef->width = NL80211_CHAN_WIDTH_40;
  30. chandef->center_freq1 = chan->center_freq + 10;
  31. break;
  32. case NL80211_CHAN_HT40MINUS:
  33. chandef->width = NL80211_CHAN_WIDTH_40;
  34. chandef->center_freq1 = chan->center_freq - 10;
  35. break;
  36. default:
  37. WARN_ON(1);
  38. }
  39. }
  40. EXPORT_SYMBOL(cfg80211_chandef_create);
  41. bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
  42. {
  43. u32 control_freq;
  44. if (!chandef->chan)
  45. return false;
  46. control_freq = chandef->chan->center_freq;
  47. switch (chandef->width) {
  48. case NL80211_CHAN_WIDTH_20:
  49. case NL80211_CHAN_WIDTH_20_NOHT:
  50. if (chandef->center_freq1 != control_freq)
  51. return false;
  52. if (chandef->center_freq2)
  53. return false;
  54. break;
  55. case NL80211_CHAN_WIDTH_40:
  56. if (chandef->center_freq1 != control_freq + 10 &&
  57. chandef->center_freq1 != control_freq - 10)
  58. return false;
  59. if (chandef->center_freq2)
  60. return false;
  61. break;
  62. case NL80211_CHAN_WIDTH_80P80:
  63. if (chandef->center_freq1 != control_freq + 30 &&
  64. chandef->center_freq1 != control_freq + 10 &&
  65. chandef->center_freq1 != control_freq - 10 &&
  66. chandef->center_freq1 != control_freq - 30)
  67. return false;
  68. if (!chandef->center_freq2)
  69. return false;
  70. /* adjacent is not allowed -- that's a 160 MHz channel */
  71. if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
  72. chandef->center_freq2 - chandef->center_freq1 == 80)
  73. return false;
  74. break;
  75. case NL80211_CHAN_WIDTH_80:
  76. if (chandef->center_freq1 != control_freq + 30 &&
  77. chandef->center_freq1 != control_freq + 10 &&
  78. chandef->center_freq1 != control_freq - 10 &&
  79. chandef->center_freq1 != control_freq - 30)
  80. return false;
  81. if (chandef->center_freq2)
  82. return false;
  83. break;
  84. case NL80211_CHAN_WIDTH_160:
  85. if (chandef->center_freq1 != control_freq + 70 &&
  86. chandef->center_freq1 != control_freq + 50 &&
  87. chandef->center_freq1 != control_freq + 30 &&
  88. chandef->center_freq1 != control_freq + 10 &&
  89. chandef->center_freq1 != control_freq - 10 &&
  90. chandef->center_freq1 != control_freq - 30 &&
  91. chandef->center_freq1 != control_freq - 50 &&
  92. chandef->center_freq1 != control_freq - 70)
  93. return false;
  94. if (chandef->center_freq2)
  95. return false;
  96. break;
  97. default:
  98. return false;
  99. }
  100. return true;
  101. }
  102. EXPORT_SYMBOL(cfg80211_chandef_valid);
  103. struct ieee80211_channel *
  104. rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
  105. int freq, enum nl80211_channel_type channel_type)
  106. {
  107. struct ieee80211_channel *chan;
  108. struct ieee80211_sta_ht_cap *ht_cap;
  109. chan = ieee80211_get_channel(&rdev->wiphy, freq);
  110. /* Primary channel not allowed */
  111. if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
  112. return NULL;
  113. if (channel_type == NL80211_CHAN_HT40MINUS &&
  114. chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
  115. return NULL;
  116. else if (channel_type == NL80211_CHAN_HT40PLUS &&
  117. chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
  118. return NULL;
  119. else if (chan->flags & IEEE80211_CHAN_NO_10MHZ)
  120. return NULL;
  121. else if (chan->flags & IEEE80211_CHAN_NO_20MHZ)
  122. return NULL;
  123. ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
  124. if (channel_type != NL80211_CHAN_NO_HT) {
  125. if (!ht_cap->ht_supported)
  126. return NULL;
  127. if (channel_type != NL80211_CHAN_HT20 &&
  128. (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
  129. ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
  130. return NULL;
  131. }
  132. return chan;
  133. }
  134. int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
  135. struct ieee80211_channel *chan,
  136. enum nl80211_channel_type channel_type)
  137. {
  138. struct ieee80211_channel *sec_chan;
  139. int diff;
  140. switch (channel_type) {
  141. case NL80211_CHAN_HT40PLUS:
  142. diff = 20;
  143. break;
  144. case NL80211_CHAN_HT40MINUS:
  145. diff = -20;
  146. break;
  147. default:
  148. return false;
  149. }
  150. sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff);
  151. if (!sec_chan)
  152. return false;
  153. /* we'll need a DFS capability later */
  154. if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
  155. IEEE80211_CHAN_PASSIVE_SCAN |
  156. IEEE80211_CHAN_NO_IBSS |
  157. IEEE80211_CHAN_RADAR))
  158. return false;
  159. return true;
  160. }
  161. EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
  162. int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
  163. struct wireless_dev *wdev, int freq,
  164. enum nl80211_channel_type channel_type)
  165. {
  166. struct ieee80211_channel *chan;
  167. int result;
  168. if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR)
  169. wdev = NULL;
  170. if (wdev) {
  171. ASSERT_WDEV_LOCK(wdev);
  172. if (!netif_running(wdev->netdev))
  173. return -ENETDOWN;
  174. }
  175. if (!rdev->ops->set_channel)
  176. return -EOPNOTSUPP;
  177. chan = rdev_freq_to_chan(rdev, freq, channel_type);
  178. if (!chan)
  179. return -EINVAL;
  180. /* Both channels should be able to initiate communication */
  181. if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC ||
  182. wdev->iftype == NL80211_IFTYPE_AP ||
  183. wdev->iftype == NL80211_IFTYPE_AP_VLAN ||
  184. wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
  185. wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
  186. switch (channel_type) {
  187. case NL80211_CHAN_HT40PLUS:
  188. case NL80211_CHAN_HT40MINUS:
  189. if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan,
  190. channel_type)) {
  191. printk(KERN_DEBUG
  192. "cfg80211: Secondary channel not "
  193. "allowed to initiate communication\n");
  194. return -EINVAL;
  195. }
  196. break;
  197. default:
  198. break;
  199. }
  200. }
  201. result = rdev->ops->set_channel(&rdev->wiphy,
  202. wdev ? wdev->netdev : NULL,
  203. chan, channel_type);
  204. if (result)
  205. return result;
  206. if (wdev)
  207. wdev->channel = chan;
  208. return 0;
  209. }