chan.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*
  2. * mac80211 - channel management
  3. */
  4. #include <linux/nl80211.h>
  5. #include <net/cfg80211.h>
  6. #include "ieee80211_i.h"
  7. static enum ieee80211_chan_mode
  8. __ieee80211_get_channel_mode(struct ieee80211_local *local,
  9. struct ieee80211_sub_if_data *ignore)
  10. {
  11. struct ieee80211_sub_if_data *sdata;
  12. lockdep_assert_held(&local->iflist_mtx);
  13. list_for_each_entry(sdata, &local->interfaces, list) {
  14. if (sdata == ignore)
  15. continue;
  16. if (!ieee80211_sdata_running(sdata))
  17. continue;
  18. switch (sdata->vif.type) {
  19. case NL80211_IFTYPE_MONITOR:
  20. continue;
  21. case NL80211_IFTYPE_STATION:
  22. if (!sdata->u.mgd.associated)
  23. continue;
  24. break;
  25. case NL80211_IFTYPE_ADHOC:
  26. if (!sdata->u.ibss.ssid_len)
  27. continue;
  28. if (!sdata->u.ibss.fixed_channel)
  29. return CHAN_MODE_HOPPING;
  30. break;
  31. case NL80211_IFTYPE_AP_VLAN:
  32. /* will also have _AP interface */
  33. continue;
  34. case NL80211_IFTYPE_AP:
  35. if (!sdata->u.ap.beacon)
  36. continue;
  37. break;
  38. case NL80211_IFTYPE_MESH_POINT:
  39. if (!sdata->wdev.mesh_id_len)
  40. continue;
  41. break;
  42. default:
  43. break;
  44. }
  45. return CHAN_MODE_FIXED;
  46. }
  47. return CHAN_MODE_UNDEFINED;
  48. }
  49. enum ieee80211_chan_mode
  50. ieee80211_get_channel_mode(struct ieee80211_local *local,
  51. struct ieee80211_sub_if_data *ignore)
  52. {
  53. enum ieee80211_chan_mode mode;
  54. mutex_lock(&local->iflist_mtx);
  55. mode = __ieee80211_get_channel_mode(local, ignore);
  56. mutex_unlock(&local->iflist_mtx);
  57. return mode;
  58. }
  59. static enum nl80211_channel_type
  60. ieee80211_get_superchan(struct ieee80211_local *local,
  61. struct ieee80211_sub_if_data *sdata)
  62. {
  63. enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
  64. struct ieee80211_sub_if_data *tmp;
  65. mutex_lock(&local->iflist_mtx);
  66. list_for_each_entry(tmp, &local->interfaces, list) {
  67. if (tmp == sdata)
  68. continue;
  69. if (!ieee80211_sdata_running(tmp))
  70. continue;
  71. switch (tmp->vif.bss_conf.channel_type) {
  72. case NL80211_CHAN_NO_HT:
  73. case NL80211_CHAN_HT20:
  74. if (superchan > tmp->vif.bss_conf.channel_type)
  75. break;
  76. superchan = tmp->vif.bss_conf.channel_type;
  77. break;
  78. case NL80211_CHAN_HT40PLUS:
  79. WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
  80. superchan = NL80211_CHAN_HT40PLUS;
  81. break;
  82. case NL80211_CHAN_HT40MINUS:
  83. WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
  84. superchan = NL80211_CHAN_HT40MINUS;
  85. break;
  86. }
  87. }
  88. mutex_unlock(&local->iflist_mtx);
  89. return superchan;
  90. }
  91. static bool
  92. ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
  93. enum nl80211_channel_type chantype2,
  94. enum nl80211_channel_type *compat)
  95. {
  96. /*
  97. * start out with chantype1 being the result,
  98. * overwriting later if needed
  99. */
  100. if (compat)
  101. *compat = chantype1;
  102. switch (chantype1) {
  103. case NL80211_CHAN_NO_HT:
  104. if (compat)
  105. *compat = chantype2;
  106. break;
  107. case NL80211_CHAN_HT20:
  108. /*
  109. * allow any change that doesn't go to no-HT
  110. * (if it already is no-HT no change is needed)
  111. */
  112. if (chantype2 == NL80211_CHAN_NO_HT)
  113. break;
  114. if (compat)
  115. *compat = chantype2;
  116. break;
  117. case NL80211_CHAN_HT40PLUS:
  118. case NL80211_CHAN_HT40MINUS:
  119. /* allow smaller bandwidth and same */
  120. if (chantype2 == NL80211_CHAN_NO_HT)
  121. break;
  122. if (chantype2 == NL80211_CHAN_HT20)
  123. break;
  124. if (chantype2 == chantype1)
  125. break;
  126. return false;
  127. }
  128. return true;
  129. }
  130. bool ieee80211_set_channel_type(struct ieee80211_local *local,
  131. struct ieee80211_sub_if_data *sdata,
  132. enum nl80211_channel_type chantype)
  133. {
  134. enum nl80211_channel_type superchan;
  135. enum nl80211_channel_type compatchan;
  136. superchan = ieee80211_get_superchan(local, sdata);
  137. if (!ieee80211_channel_types_are_compatible(superchan, chantype,
  138. &compatchan))
  139. return false;
  140. local->_oper_channel_type = compatchan;
  141. if (sdata)
  142. sdata->vif.bss_conf.channel_type = chantype;
  143. return true;
  144. }