spectmgmt.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*
  2. * spectrum management
  3. *
  4. * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
  5. * Copyright 2002-2005, Instant802 Networks, Inc.
  6. * Copyright 2005-2006, Devicescape Software, Inc.
  7. * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  8. * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  9. * Copyright 2007-2008, Intel Corporation
  10. * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License version 2 as
  14. * published by the Free Software Foundation.
  15. */
  16. #include <linux/ieee80211.h>
  17. #include <net/wireless.h>
  18. #include <net/mac80211.h>
  19. #include "ieee80211_i.h"
  20. #include "sta_info.h"
  21. #include "wme.h"
  22. static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
  23. struct ieee80211_msrment_ie *request_ie,
  24. const u8 *da, const u8 *bssid,
  25. u8 dialog_token)
  26. {
  27. struct ieee80211_local *local = sdata->local;
  28. struct sk_buff *skb;
  29. struct ieee80211_mgmt *msr_report;
  30. skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom +
  31. sizeof(struct ieee80211_msrment_ie));
  32. if (!skb) {
  33. printk(KERN_ERR "%s: failed to allocate buffer for "
  34. "measurement report frame\n", sdata->dev->name);
  35. return;
  36. }
  37. skb_reserve(skb, local->hw.extra_tx_headroom);
  38. msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24);
  39. memset(msr_report, 0, 24);
  40. memcpy(msr_report->da, da, ETH_ALEN);
  41. memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN);
  42. memcpy(msr_report->bssid, bssid, ETH_ALEN);
  43. msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
  44. IEEE80211_STYPE_ACTION);
  45. skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement));
  46. msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
  47. msr_report->u.action.u.measurement.action_code =
  48. WLAN_ACTION_SPCT_MSR_RPRT;
  49. msr_report->u.action.u.measurement.dialog_token = dialog_token;
  50. msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT;
  51. msr_report->u.action.u.measurement.length =
  52. sizeof(struct ieee80211_msrment_ie);
  53. memset(&msr_report->u.action.u.measurement.msr_elem, 0,
  54. sizeof(struct ieee80211_msrment_ie));
  55. msr_report->u.action.u.measurement.msr_elem.token = request_ie->token;
  56. msr_report->u.action.u.measurement.msr_elem.mode |=
  57. IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
  58. msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
  59. ieee80211_tx_skb(sdata, skb, 1);
  60. }
  61. void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  62. struct ieee80211_mgmt *mgmt,
  63. size_t len)
  64. {
  65. /*
  66. * Ignoring measurement request is spec violation.
  67. * Mandatory measurements must be reported optional
  68. * measurements might be refused or reported incapable
  69. * For now just refuse
  70. * TODO: Answer basic measurement as unmeasured
  71. */
  72. ieee80211_send_refuse_measurement_request(sdata,
  73. &mgmt->u.action.u.measurement.msr_elem,
  74. mgmt->sa, mgmt->bssid,
  75. mgmt->u.action.u.measurement.dialog_token);
  76. }
  77. void ieee80211_chswitch_work(struct work_struct *work)
  78. {
  79. struct ieee80211_sub_if_data *sdata =
  80. container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
  81. struct ieee80211_bss *bss;
  82. struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  83. if (!netif_running(sdata->dev))
  84. return;
  85. bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
  86. sdata->local->hw.conf.channel->center_freq,
  87. ifmgd->ssid, ifmgd->ssid_len);
  88. if (!bss)
  89. goto exit;
  90. sdata->local->oper_channel = sdata->local->csa_channel;
  91. /* XXX: shouldn't really modify cfg80211-owned data! */
  92. if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
  93. bss->cbss.channel = sdata->local->oper_channel;
  94. ieee80211_rx_bss_put(sdata->local, bss);
  95. exit:
  96. ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
  97. ieee80211_wake_queues_by_reason(&sdata->local->hw,
  98. IEEE80211_QUEUE_STOP_REASON_CSA);
  99. }
  100. void ieee80211_chswitch_timer(unsigned long data)
  101. {
  102. struct ieee80211_sub_if_data *sdata =
  103. (struct ieee80211_sub_if_data *) data;
  104. struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  105. queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
  106. }
  107. void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
  108. struct ieee80211_channel_sw_ie *sw_elem,
  109. struct ieee80211_bss *bss)
  110. {
  111. struct ieee80211_channel *new_ch;
  112. struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  113. int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
  114. /* FIXME: Handle ADHOC later */
  115. if (sdata->vif.type != NL80211_IFTYPE_STATION)
  116. return;
  117. if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
  118. return;
  119. if (sdata->local->sw_scanning || sdata->local->hw_scanning)
  120. return;
  121. /* Disregard subsequent beacons if we are already running a timer
  122. processing a CSA */
  123. if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
  124. return;
  125. new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
  126. if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
  127. return;
  128. sdata->local->csa_channel = new_ch;
  129. if (sw_elem->count <= 1) {
  130. queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
  131. } else {
  132. ieee80211_stop_queues_by_reason(&sdata->local->hw,
  133. IEEE80211_QUEUE_STOP_REASON_CSA);
  134. ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
  135. mod_timer(&ifmgd->chswitch_timer,
  136. jiffies +
  137. msecs_to_jiffies(sw_elem->count *
  138. bss->cbss.beacon_interval));
  139. }
  140. }
  141. void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
  142. u16 capab_info, u8 *pwr_constr_elem,
  143. u8 pwr_constr_elem_len)
  144. {
  145. struct ieee80211_conf *conf = &sdata->local->hw.conf;
  146. if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
  147. return;
  148. /* Power constraint IE length should be 1 octet */
  149. if (pwr_constr_elem_len != 1)
  150. return;
  151. if ((*pwr_constr_elem <= conf->channel->max_power) &&
  152. (*pwr_constr_elem != sdata->local->power_constr_level)) {
  153. sdata->local->power_constr_level = *pwr_constr_elem;
  154. ieee80211_hw_config(sdata->local, 0);
  155. }
  156. }