chan.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. }
  145. static struct ieee80211_chanctx *
  146. ieee80211_find_chanctx(struct ieee80211_local *local,
  147. struct ieee80211_channel *channel,
  148. enum nl80211_channel_type channel_type,
  149. enum ieee80211_chanctx_mode mode)
  150. {
  151. struct ieee80211_chanctx *ctx;
  152. lockdep_assert_held(&local->chanctx_mtx);
  153. if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
  154. return NULL;
  155. if (WARN_ON(!channel))
  156. return NULL;
  157. list_for_each_entry(ctx, &local->chanctx_list, list) {
  158. if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
  159. continue;
  160. if (ctx->conf.channel != channel)
  161. continue;
  162. if (ctx->conf.channel_type != channel_type)
  163. continue;
  164. return ctx;
  165. }
  166. return NULL;
  167. }
  168. static struct ieee80211_chanctx *
  169. ieee80211_new_chanctx(struct ieee80211_local *local,
  170. struct ieee80211_channel *channel,
  171. enum nl80211_channel_type channel_type,
  172. enum ieee80211_chanctx_mode mode)
  173. {
  174. struct ieee80211_chanctx *ctx;
  175. lockdep_assert_held(&local->chanctx_mtx);
  176. ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
  177. if (!ctx)
  178. return ERR_PTR(-ENOMEM);
  179. ctx->conf.channel = channel;
  180. ctx->conf.channel_type = channel_type;
  181. ctx->mode = mode;
  182. list_add(&ctx->list, &local->chanctx_list);
  183. return ctx;
  184. }
  185. static void ieee80211_free_chanctx(struct ieee80211_local *local,
  186. struct ieee80211_chanctx *ctx)
  187. {
  188. lockdep_assert_held(&local->chanctx_mtx);
  189. WARN_ON_ONCE(ctx->refcount != 0);
  190. list_del(&ctx->list);
  191. kfree_rcu(ctx, rcu_head);
  192. }
  193. static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
  194. struct ieee80211_chanctx *ctx)
  195. {
  196. struct ieee80211_local *local __maybe_unused = sdata->local;
  197. lockdep_assert_held(&local->chanctx_mtx);
  198. rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
  199. ctx->refcount++;
  200. return 0;
  201. }
  202. static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
  203. struct ieee80211_chanctx *ctx)
  204. {
  205. struct ieee80211_local *local __maybe_unused = sdata->local;
  206. lockdep_assert_held(&local->chanctx_mtx);
  207. ctx->refcount--;
  208. rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
  209. }
  210. static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
  211. {
  212. struct ieee80211_local *local = sdata->local;
  213. struct ieee80211_chanctx_conf *conf;
  214. struct ieee80211_chanctx *ctx;
  215. lockdep_assert_held(&local->chanctx_mtx);
  216. conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
  217. lockdep_is_held(&local->chanctx_mtx));
  218. if (!conf)
  219. return;
  220. ctx = container_of(conf, struct ieee80211_chanctx, conf);
  221. ieee80211_unassign_vif_chanctx(sdata, ctx);
  222. if (ctx->refcount == 0)
  223. ieee80211_free_chanctx(local, ctx);
  224. }
  225. int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
  226. struct ieee80211_channel *channel,
  227. enum nl80211_channel_type channel_type,
  228. enum ieee80211_chanctx_mode mode)
  229. {
  230. struct ieee80211_local *local = sdata->local;
  231. struct ieee80211_chanctx *ctx;
  232. int ret;
  233. mutex_lock(&local->chanctx_mtx);
  234. __ieee80211_vif_release_channel(sdata);
  235. ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
  236. if (!ctx)
  237. ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
  238. if (IS_ERR(ctx)) {
  239. ret = PTR_ERR(ctx);
  240. goto out;
  241. }
  242. ret = ieee80211_assign_vif_chanctx(sdata, ctx);
  243. if (ret) {
  244. /* if assign fails refcount stays the same */
  245. if (ctx->refcount == 0)
  246. ieee80211_free_chanctx(local, ctx);
  247. goto out;
  248. }
  249. out:
  250. mutex_unlock(&local->chanctx_mtx);
  251. return ret;
  252. }
  253. void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
  254. {
  255. mutex_lock(&sdata->local->chanctx_mtx);
  256. __ieee80211_vif_release_channel(sdata);
  257. mutex_unlock(&sdata->local->chanctx_mtx);
  258. }