* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/export.h>
#include <linux/moduleparam.h>
module_param_named(debug, ath5k_debug, uint, 0);
- static int ath5k_debugfs_open(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
-
-
/* debugfs: registers */
struct reg {
if (strncmp(buf, "disable", 7) == 0) {
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
- printk(KERN_INFO "debugfs disable beacons\n");
+ pr_info("debugfs disable beacons\n");
} else if (strncmp(buf, "enable", 6) == 0) {
AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
- printk(KERN_INFO "debugfs enable beacons\n");
+ pr_info("debugfs enable beacons\n");
}
return count;
}
static const struct file_operations fops_beacon = {
.read = read_file_beacon,
.write = write_file_beacon,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_reset = {
.write = write_file_reset,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
static const struct file_operations fops_debug = {
.read = read_file_debug,
.write = write_file_debug,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
if (strncmp(buf, "diversity", 9) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
- printk(KERN_INFO "ath5k debug: enable diversity\n");
+ pr_info("debug: enable diversity\n");
} else if (strncmp(buf, "fixed-a", 7) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
- printk(KERN_INFO "ath5k debugfs: fixed antenna A\n");
+ pr_info("debug: fixed antenna A\n");
} else if (strncmp(buf, "fixed-b", 7) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
- printk(KERN_INFO "ath5k debug: fixed antenna B\n");
+ pr_info("debug: fixed antenna B\n");
} else if (strncmp(buf, "clear", 5) == 0) {
for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) {
ah->stats.antenna_rx[i] = 0;
ah->stats.antenna_tx[i] = 0;
}
- printk(KERN_INFO "ath5k debug: cleared antenna stats\n");
+ pr_info("debug: cleared antenna stats\n");
}
return count;
}
static const struct file_operations fops_antenna = {
.read = read_file_antenna,
.write = write_file_antenna,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_misc = {
.read = read_file_misc,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
};
st->txerr_fifo = 0;
st->txerr_filt = 0;
st->tx_all_count = 0;
- printk(KERN_INFO "ath5k debug: cleared frameerrors stats\n");
+ pr_info("debug: cleared frameerrors stats\n");
}
return count;
}
static const struct file_operations fops_frameerrors = {
.read = read_file_frameerrors,
.write = write_file_frameerrors,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_ani = {
.read = read_file_ani,
.write = write_file_ani,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_queue = {
.read = read_file_queue,
.write = write_file_queue,
- .open = ath5k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
#define REG_READ_D(_ah, _reg) \
ath9k_hw_common(_ah)->ops->read((_ah), (_reg))
- static int ath9k_debugfs_open(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
static const struct file_operations fops_debug = {
.read = read_file_debug,
.write = write_file_debug,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_tx_chainmask = {
.read = read_file_tx_chainmask,
.write = write_file_tx_chainmask,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_rx_chainmask = {
.read = read_file_rx_chainmask,
.write = write_file_rx_chainmask,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_disable_ani = {
.read = read_file_disable_ani,
.write = write_file_disable_ani,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_dma = {
.read = read_file_dma,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_interrupt = {
.read = read_file_interrupt,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
PR("hw-put-tx-buf: ", puttxbuf);
PR("hw-tx-start: ", txstart);
PR("hw-tx-proc-desc: ", txprocdesc);
+ PR("TX-Failed: ", txfailed);
len += snprintf(buf + len, size - len,
"%s%11p%11p%10p%10p\n", "txq-memory-address:",
sc->tx.txq_map[WME_AC_BE],
static const struct file_operations fops_xmit = {
.read = read_file_xmit,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_stations = {
.read = read_file_stations,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_misc = {
.read = read_file_misc,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_reset = {
.read = read_file_reset,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
len += snprintf(buf + len, size - len,
"%22s : %10u\n", "DECRYPT BUSY ERR",
sc->debug.stats.rxstats.decrypt_busy_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-LENGTH-ERR",
+ sc->debug.stats.rxstats.rx_len_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-OOM-ERR",
+ sc->debug.stats.rxstats.rx_oom_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-RATE-ERR",
+ sc->debug.stats.rxstats.rx_rate_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-DROP-RXFLUSH",
+ sc->debug.stats.rxstats.rx_drop_rxflush);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-TOO-MANY-FRAGS",
+ sc->debug.stats.rxstats.rx_too_many_frags_err);
PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING);
len += snprintf(buf + len, size - len,
"%22s : %10u\n", "RX-Bytes-All",
sc->debug.stats.rxstats.rx_bytes_all);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-Beacons",
+ sc->debug.stats.rxstats.rx_beacons);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-Frags",
+ sc->debug.stats.rxstats.rx_frags);
if (len > size)
len = size;
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{
-#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
[sc->debug.rsidx].c)
#endif
-#undef RX_STAT_INC
#undef RX_PHY_ERR_INC
#undef RX_SAMP_DBG
}
static const struct file_operations fops_recv = {
.read = read_file_recv,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_regidx = {
.read = read_file_regidx,
.write = write_file_regidx,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_regval = {
.read = read_file_regval,
.write = write_file_regval,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_dump_nfcal = {
.read = read_file_dump_nfcal,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_base_eeprom = {
.read = read_file_base_eeprom,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static const struct file_operations fops_modal_eeprom = {
.read = read_file_modal_eeprom,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
if (--sc->ps_usecount != 0)
goto unlock;
- if (sc->ps_flags & PS_WAIT_FOR_TX_ACK)
- goto unlock;
-
- if (sc->ps_idle)
+ if (sc->ps_idle && (sc->ps_flags & PS_WAIT_FOR_TX_ACK))
mode = ATH9K_PM_FULL_SLEEP;
else if (sc->ps_enabled &&
!(sc->ps_flags & (PS_WAIT_FOR_BEACON |
PS_WAIT_FOR_CAB |
- PS_WAIT_FOR_PSPOLL_DATA)))
+ PS_WAIT_FOR_PSPOLL_DATA |
+ PS_WAIT_FOR_TX_ACK)))
mode = ATH9K_PM_NETWORK_SLEEP;
else
goto unlock;
sc->hw_busy_count = 0;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah);
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
+ ath_start_rx_poll(sc, 3);
if (!common->disable_ani)
ath_start_ani(common);
}
an->sta = sta;
an->vif = vif;
- if (sta->ht_cap.ht_supported) {
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
ath_tx_node_init(sc, an);
an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
sta->ht_cap.ampdu_factor);
an->sta = NULL;
#endif
- if (sta->ht_cap.ht_supported)
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
ath_tx_node_cleanup(sc, an);
}
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
unsigned long flags;
int busy;
+ u8 is_alive, nbeacon = 1;
ath9k_ps_wakeup(sc);
- if (ath9k_hw_check_alive(sc->sc_ah))
+ is_alive = ath9k_hw_check_alive(sc->sc_ah);
+
+ if (is_alive && !AR_SREV_9300(sc->sc_ah))
goto out;
+ else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
+ ath_dbg(common, RESET,
+ "DCU stuck is detected. Schedule chip reset\n");
+ RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
+ goto sched_reset;
+ }
spin_lock_irqsave(&common->cc_lock, flags);
busy = ath_update_survey_stats(sc);
if (busy >= 99) {
if (++sc->hw_busy_count >= 3) {
RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
- ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ goto sched_reset;
}
-
- } else if (busy >= 0)
+ } else if (busy >= 0) {
sc->hw_busy_count = 0;
+ nbeacon = 3;
+ }
+ ath_start_rx_poll(sc, nbeacon);
+ goto out;
+
+sched_reset:
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
out:
ath9k_ps_restore(sc);
}
if (ath_tx_start(hw, skb, &txctl) != 0) {
ath_dbg(common, XMIT, "TX failed\n");
+ TX_STAT_INC(txctl.txq->axq_qnum, txfailed);
goto exit;
}
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
+ del_timer_sync(&sc->rx_poll_timer);
if (sc->sc_flags & SC_OP_INVALID) {
ath_dbg(common, ANY, "Device not present\n");
}
}
+void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
+{
+ if (!AR_SREV_9300(sc->sc_ah))
+ return;
+
+ if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
+ return;
+
+ mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
+ (nbeacon * sc->cur_beacon_conf.beacon_interval));
+}
+
+void ath_rx_poll(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *)data;
+
+ ieee80211_queue_work(sc->hw, &sc->hw_check_work);
+}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+ ath_start_rx_poll(sc, 3);
+
if (!common->disable_ani) {
sc->sc_flags |= SC_OP_ANI_RUN;
ath_start_ani(common);
/* Stop ANI */
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
memset(&sc->caldata, 0, sizeof(sc->caldata));
}
}
} else {
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
}
}
static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- u32 changed, enum nl80211_channel_type oper_chan_type)
+ u32 changed)
{
struct ath_softc *sc = priv;
struct ath_rate_priv *ath_rc_priv = priv_sta;
/* FIXME: Handle AP mode later when we support CWM */
- if (changed & IEEE80211_RC_HT_CHANGED) {
+ if (changed & IEEE80211_RC_BW_CHANGED) {
if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
return;
- if (oper_chan_type == NL80211_CHAN_HT40MINUS ||
- oper_chan_type == NL80211_CHAN_HT40PLUS)
+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
oper_cw40 = true;
if (oper_cw40)
#ifdef CONFIG_ATH9K_DEBUGFS
- static int ath9k_debugfs_open(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
-
static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
static const struct file_operations fops_rcstat = {
.read = read_file_rcstat,
- .open = ath9k_debugfs_open,
+ .open = simple_open,
.owner = THIS_MODULE
};
tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI) ||
tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ||
tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA) ||
- tbl_type.ant_type != info->antenna_sel_tx ||
+ tbl_type.ant_type != info->status.antenna ||
!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)
|| !!(tx_rate & RATE_MCS_GF_MSK) !=
!!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD) || rs_idx != mac_idx) {
}
#ifdef CONFIG_MAC80211_DEBUGFS
- static int
- il4965_open_file_generic(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
static void
il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx)
static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
.write = il4965_rs_sta_dbgfs_scale_table_write,
.read = il4965_rs_sta_dbgfs_scale_table_read,
- .open = il4965_open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
.read = il4965_rs_sta_dbgfs_stats_table_read,
- .open = il4965_open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
.read = il4965_rs_sta_dbgfs_rate_scale_data_read,
- .open = il4965_open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
(tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
(tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
(tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) ||
- (tbl_type.ant_type != info->antenna_sel_tx) ||
+ (tbl_type.ant_type != info->status.antenna) ||
(!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
(!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
(rs_index != mac_index)) {
(lq_sta->total_success > lq_sta->max_success_limit) ||
((!lq_sta->search_better_tbl) && (lq_sta->flush_timer)
&& (flush_interval_passed))) {
- IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n:",
+ IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n",
lq_sta->total_failed,
lq_sta->total_success,
flush_interval_passed);
}
#ifdef CONFIG_MAC80211_DEBUGFS
- static int open_file_generic(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
u32 *rate_n_flags, int index)
{
static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
.write = rs_sta_dbgfs_scale_table_write,
.read = rs_sta_dbgfs_scale_table_read,
- .open = open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
.read = rs_sta_dbgfs_stats_table_read,
- .open = open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
.read = rs_sta_dbgfs_rate_scale_data_read,
- .open = open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
size_t count, loff_t *ppos);
- static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
-
#define DEBUGFS_READ_FILE_OPS(name) \
DEBUGFS_READ_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.read = iwl_dbgfs_##name##_read, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
IWL_ERR(priv, "No uCode has been loadded.\n");
return -EINVAL;
}
- img = &priv->fw->img[priv->shrd->ucode_type];
+ img = &priv->fw->img[priv->cur_ucode];
priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
}
len = priv->dbgfs_sram_len;
i, station->sta.sta.addr,
station->sta.station_flags_msk);
pos += scnprintf(buf + pos, bufsz - pos,
- "TID\tseq_num\trate_n_flags\n");
+ "TID seqno next_rclmd "
+ "rate_n_flags state txq\n");
for (j = 0; j < IWL_MAX_TID_COUNT; j++) {
tid_data = &priv->tid_data[i][j];
pos += scnprintf(buf + pos, bufsz - pos,
- "%d:\t%#x\t%#x",
+ "%d: 0x%.4x 0x%.4x 0x%.8x "
+ "%d %.2d",
j, tid_data->seq_number,
- tid_data->agg.rate_n_flags);
+ tid_data->next_reclaimed,
+ tid_data->agg.rate_n_flags,
+ tid_data->agg.state,
+ tid_data->agg.txq_id);
if (tid_data->agg.wait_for_ba)
pos += scnprintf(buf + pos, bufsz - pos,
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
test_bit(STATUS_SCAN_HW, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
- test_bit(STATUS_POWER_PMI, &priv->shrd->status));
+ test_bit(STATUS_POWER_PMI, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
- test_bit(STATUS_FW_ERROR, &priv->shrd->status));
+ test_bit(STATUS_FW_ERROR, &priv->status));
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
return count;
}
+static ssize_t iwl_dbgfs_log_event_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char *buf;
+ int pos = 0;
+ ssize_t ret = -ENOMEM;
+
+ ret = pos = iwl_dump_nic_event_log(priv, true, &buf, true);
+ if (buf) {
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ }
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_log_event_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ u32 event_log_flag;
+ char buf[8];
+ int buf_size;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &event_log_flag) != 1)
+ return -EFAULT;
+ if (event_log_flag == 1)
+ iwl_dump_nic_event_log(priv, true, NULL, false);
+
+ return count;
+}
+
DEBUGFS_READ_FILE_OPS(rx_statistics);
DEBUGFS_READ_FILE_OPS(tx_statistics);
DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
DEBUGFS_READ_FILE_OPS(reply_tx_error);
DEBUGFS_WRITE_FILE_OPS(echo_test);
+DEBUGFS_READ_WRITE_FILE_OPS(log_event);
/*
* Create the debugfs files and directories
DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR);
+
if (iwl_advanced_bt_coexist(priv))
DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR);
FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
- FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
rb_size|
(rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
(rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
}
static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq,
- int slots_num, u32 txq_id)
+ int slots_num, u32 txq_id)
{
int ret;
txq->need_update = 0;
memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num);
- /*
- * For the default queues 0-3, set up the swq_id
- * already -- all others need to get one later
- * (if they need one at all).
- */
- if (txq_id < 4)
- iwl_set_swq_id(txq, txq_id, txq_id);
-
/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
* iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
return ret;
}
-#define IWL_AC_UNSET -1
-
-struct queue_to_fifo_ac {
- s8 fifo, ac;
-};
-
-static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = {
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
-};
-
-static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = {
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
- { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, },
- { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_BE_IPAN, 2, },
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
- { IWL_TX_FIFO_AUX, IWL_AC_UNSET, },
-};
-
-static const u8 iwlagn_bss_ac_to_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
-};
-static const u8 iwlagn_bss_ac_to_queue[] = {
- 0, 1, 2, 3,
-};
-static const u8 iwlagn_pan_ac_to_fifo[] = {
- IWL_TX_FIFO_VO_IPAN,
- IWL_TX_FIFO_VI_IPAN,
- IWL_TX_FIFO_BE_IPAN,
- IWL_TX_FIFO_BK_IPAN,
-};
-static const u8 iwlagn_pan_ac_to_queue[] = {
- 7, 6, 5, 4,
-};
-
/*
* ucode
*/
const struct fw_img *fw)
{
int ret;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill;
- trans_pcie->ac_to_queue[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_queue;
- trans_pcie->ac_to_queue[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_queue;
-
- trans_pcie->ac_to_fifo[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_fifo;
- trans_pcie->ac_to_fifo[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_fifo;
-
- trans_pcie->mcast_queue[IWL_RXON_CTX_BSS] = 0;
- trans_pcie->mcast_queue[IWL_RXON_CTX_PAN] = IWL_IPAN_MCAST_QUEUE;
-
/* This may fail if AMT took ownership of the device */
if (iwl_prepare_card_hw(trans)) {
IWL_WARN(trans, "Exit HW not ready\n");
static void iwl_tx_start(struct iwl_trans *trans)
{
- const struct queue_to_fifo_ac *queue_to_fifo;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 a;
unsigned long flags;
int i, chan;
/* Activate all Tx DMA/FIFO channels */
iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7));
- /* map queues to FIFOs */
- if (trans->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS))
- queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo;
- else
- queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
-
iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0);
- /* make sure all queue are not stopped */
- memset(&trans_pcie->queue_stopped[0], 0,
- sizeof(trans_pcie->queue_stopped));
- for (i = 0; i < 4; i++)
- atomic_set(&trans_pcie->queue_stop_count[i], 0);
+ /* make sure all queue are not stopped/used */
+ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
+ memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
- /* reset to 0 to enable all the queue first */
- trans_pcie->txq_ctx_active_msk = 0;
+ for (i = 0; i < trans_pcie->n_q_to_fifo; i++) {
+ int fifo = trans_pcie->setup_q_to_fifo[i];
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) <
- IWLAGN_FIRST_AMPDU_QUEUE);
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) <
- IWLAGN_FIRST_AMPDU_QUEUE);
+ set_bit(i, trans_pcie->queue_used);
- for (i = 0; i < IWLAGN_FIRST_AMPDU_QUEUE; i++) {
- int fifo = queue_to_fifo[i].fifo;
- int ac = queue_to_fifo[i].ac;
-
- iwl_txq_ctx_activate(trans_pcie, i);
-
- if (fifo == IWL_TX_FIFO_UNUSED)
- continue;
-
- if (ac != IWL_AC_UNSET)
- iwl_set_swq_id(&trans_pcie->txq[i], ac, i);
iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i],
- fifo, 0);
+ fifo, true);
}
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
}
static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
- u8 sta_id, u8 tid)
+ struct iwl_device_cmd *dev_cmd, int txq_id)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
struct iwl_cmd_meta *out_meta;
struct iwl_tx_queue *txq;
struct iwl_queue *q;
-
dma_addr_t phys_addr = 0;
dma_addr_t txcmd_phys;
dma_addr_t scratch_phys;
u16 len, firstlen, secondlen;
u8 wait_write_ptr = 0;
- u8 txq_id;
- bool is_agg = false;
__le16 fc = hdr->frame_control;
u8 hdr_len = ieee80211_hdrlen(fc);
u16 __maybe_unused wifi_seq;
- /*
- * Send this frame after DTIM -- there's a special queue
- * reserved for this for contexts that support AP mode.
- */
- if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
- txq_id = trans_pcie->mcast_queue[ctx];
-
- /*
- * The microcode will clear the more data
- * bit in the last frame it transmits.
- */
- hdr->frame_control |=
- cpu_to_le16(IEEE80211_FCTL_MOREDATA);
- } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
- txq_id = IWL_AUX_QUEUE;
- else
- txq_id =
- trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)];
-
- /* aggregation is on for this <sta,tid> */
- if (info->flags & IEEE80211_TX_CTL_AMPDU) {
- WARN_ON(tid >= IWL_MAX_TID_COUNT);
- txq_id = trans_pcie->agg_txq[sta_id][tid];
- is_agg = true;
- }
-
txq = &trans_pcie->txq[txq_id];
q = &txq->q;
- spin_lock(&txq->lock);
+ if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
- /* In AGG mode, the index in the ring must correspond to the WiFi
- * sequence number. This is a HW requirements to help the SCD to parse
- * the BA.
- * Check here that the packets are in the right place on the ring.
- */
-#ifdef CONFIG_IWLWIFI_DEBUG
- wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- WARN_ONCE(is_agg && ((wifi_seq & 0xff) != q->write_ptr),
- "Q: %d WiFi Seq %d tfdNum %d",
- txq_id, wifi_seq, q->write_ptr);
-#endif
+ spin_lock(&txq->lock);
/* Set up driver data for this TFD */
txq->skbs[q->write_ptr] = skb;
iwl_enable_rfkill_int(trans);
}
-static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
- int txq_id, int ssn, struct sk_buff_head *skbs)
+static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
+ struct sk_buff_head *skbs)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
txq->time_stamp = jiffies;
- if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE &&
- tid != IWL_TID_NON_QOS &&
- txq_id != trans_pcie->agg_txq[sta_id][tid])) {
- /*
- * FIXME: this is a uCode bug which need to be addressed,
- * log the information and return for now.
- * Since it is can possibly happen very often and in order
- * not to fill the syslog, don't use IWL_ERR or IWL_WARN
- */
- IWL_DEBUG_TX_QUEUES(trans, "Bad queue mapping txq_id %d, "
- "agg_txq[sta_id[tid] %d", txq_id,
- trans_pcie->agg_txq[sta_id][tid]);
- spin_unlock(&txq->lock);
- return 1;
- }
-
if (txq->q.read_ptr != tfd_num) {
- IWL_DEBUG_TX_REPLY(trans, "[Q %d | AC %d] %d -> %d (%d)\n",
- txq_id, iwl_get_queue_ac(txq), txq->q.read_ptr,
- tfd_num, ssn);
+ IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n",
+ txq_id, txq->q.read_ptr, tfd_num, ssn);
freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs);
if (iwl_queue_space(&txq->q) > txq->q.low_mark)
iwl_wake_queue(trans, txq);
}
spin_unlock(&txq->lock);
- return 0;
}
static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
}
static void iwl_trans_pcie_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg)
+ const struct iwl_trans_config *trans_cfg)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
if (trans_pcie->n_no_reclaim_cmds)
memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
trans_pcie->n_no_reclaim_cmds * sizeof(u8));
+
+ trans_pcie->n_q_to_fifo = trans_cfg->n_queue_to_fifo;
+
+ if (WARN_ON(trans_pcie->n_q_to_fifo > IWL_MAX_HW_QUEUES))
+ trans_pcie->n_q_to_fifo = IWL_MAX_HW_QUEUES;
+
+ /* at least the command queue must be mapped */
+ WARN_ON(!trans_pcie->n_q_to_fifo);
+
+ memcpy(trans_pcie->setup_q_to_fifo, trans_cfg->queue_to_fifo,
+ trans_pcie->n_q_to_fifo * sizeof(u8));
}
static void iwl_trans_pcie_free(struct iwl_trans *trans)
kfree(trans);
}
+static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (state)
+ set_bit(STATUS_POWER_PMI, &trans_pcie->status);
+ else
+ clear_bit(STATUS_POWER_PMI, &trans_pcie->status);
+}
+
#ifdef CONFIG_PM_SLEEP
static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
{
size_t count, loff_t *ppos);
- static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
-
#define DEBUGFS_READ_FILE_OPS(name) \
DEBUGFS_READ_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.read = iwl_dbgfs_##name##_read, \
- .open = iwl_dbgfs_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
};
txq = &trans_pcie->txq[cnt];
q = &txq->q;
pos += scnprintf(buf + pos, bufsz - pos,
- "hwq %.2d: read=%u write=%u stop=%d"
- " swq_id=%#.2x (ac %d/hwq %d)\n",
+ "hwq %.2d: read=%u write=%u use=%d stop=%d\n",
cnt, q->read_ptr, q->write_ptr,
- !!test_bit(cnt, trans_pcie->queue_stopped),
- txq->swq_id, txq->swq_id & 3,
- (txq->swq_id >> 2) & 0x1f);
- if (cnt >= 4)
- continue;
- /* for the ACs, display the stop count too */
- pos += scnprintf(buf + pos, bufsz - pos,
- " stop-count: %d\n",
- atomic_read(&trans_pcie->queue_stop_count[cnt]));
+ !!test_bit(cnt, trans_pcie->queue_used),
+ !!test_bit(cnt, trans_pcie->queue_stopped));
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
-static ssize_t iwl_dbgfs_log_event_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- char *buf;
- int pos = 0;
- ssize_t ret = -ENOMEM;
-
- ret = pos = iwl_dump_nic_event_log(trans, true, &buf, true);
- if (buf) {
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- }
- return ret;
-}
-
-static ssize_t iwl_dbgfs_log_event_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- u32 event_log_flag;
- char buf[8];
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &event_log_flag) != 1)
- return -EFAULT;
- if (event_log_flag == 1)
- iwl_dump_nic_event_log(trans, true, NULL, false);
-
- return count;
-}
-
static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos) {
return ret;
}
-DEBUGFS_READ_WRITE_FILE_OPS(log_event);
DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
DEBUGFS_READ_FILE_OPS(fh_reg);
DEBUGFS_READ_FILE_OPS(rx_queue);
{
DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(log_event, dir, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
.reclaim = iwl_trans_pcie_reclaim,
.tx_agg_disable = iwl_trans_pcie_tx_agg_disable,
- .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc,
.tx_agg_setup = iwl_trans_pcie_tx_agg_setup,
.free = iwl_trans_pcie_free,
.write32 = iwl_trans_pcie_write32,
.read32 = iwl_trans_pcie_read32,
.configure = iwl_trans_pcie_configure,
+ .set_pmi = iwl_trans_pcie_set_pmi,
};
struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd,
* @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
*/
enum ieee80211_max_queues {
- IEEE80211_MAX_QUEUES = 4,
+ IEEE80211_MAX_QUEUES = 16,
};
+#define IEEE80211_INVAL_HW_QUEUE 0xff
+
/**
* enum ieee80211_ac_numbers - AC numbers as used in mac80211
* @IEEE80211_AC_VO: voice
* @channel_type: Channel type for this BSS -- the hardware might be
* configured for HT40+ while this BSS only uses no-HT, for
* example.
- * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
+ * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation.
* This field is only valid when the channel type is one of the HT types.
* @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
* implies disabled
*
* @flags: transmit info flags, defined above
* @band: the band to transmit on (use for checking for races)
- * @antenna_sel_tx: antenna to use, 0 for automatic diversity
+ * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
* @ack_frame_id: internal frame ID for TX status, used internally
* @control: union for control data
* @status: union for status data
u32 flags;
u8 band;
- u8 antenna_sel_tx;
+ u8 hw_queue;
u16 ack_frame_id;
u8 ampdu_ack_len;
int ack_signal;
u8 ampdu_len;
- /* 15 bytes free */
+ u8 antenna;
+ /* 14 bytes free */
} status;
struct {
struct ieee80211_tx_rate driver_rates[
* these need to be set (or cleared) when the interface is added
* or, if supported by the driver, the interface type is changed
* at runtime, mac80211 will never touch this field
+ * @hw_queue: hardware queue for each AC
+ * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
*/
struct ieee80211_bss_conf bss_conf;
u8 addr[ETH_ALEN];
bool p2p;
+
+ u8 cab_queue;
+ u8 hw_queue[IEEE80211_NUM_ACS];
+
u32 driver_flags;
+
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
* @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while
* being idle (i.e. mac80211 doesn't have to go idle-off during the
* the scan).
+ *
+ * @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of
+ * a virtual monitor interface when monitor interfaces are the only
+ * active interfaces.
+ *
+ * @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
+ * queue mapping in order to use different queues (not just one per AC)
+ * for different virtual interfaces. See the doc section on HW queue
+ * control for more details.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11,
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
- /* reuse bit 14 */
+ IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
- /* reuse bit 20 */
+ IEEE80211_HW_QUEUE_CONTROL = 1<<20,
IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
IEEE80211_HW_AP_LINK_PS = 1<<22,
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23,
* @max_tx_aggregation_subframes: maximum number of subframes in an
* aggregate an HT driver will transmit, used by the peer as a
* hint to size its reorder buffer.
+ *
+ * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
+ * (if %IEEE80211_HW_QUEUE_CONTROL is set)
*/
struct ieee80211_hw {
struct ieee80211_conf conf;
u8 max_rate_tries;
u8 max_rx_aggregation_subframes;
u8 max_tx_aggregation_subframes;
+ u8 offchannel_tx_hw_queue;
};
/**
ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c)
{
- if (WARN_ON(c->control.rates[0].idx < 0))
+ if (WARN_ON_ONCE(c->control.rates[0].idx < 0))
return NULL;
return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx];
}
* The driver may also use ieee80211_sta_eosp_irqsafe() in this case.
*/
+/**
+ * DOC: HW queue control
+ *
+ * Before HW queue control was introduced, mac80211 only had a single static
+ * assignment of per-interface AC software queues to hardware queues. This
+ * was problematic for a few reasons:
+ * 1) off-channel transmissions might get stuck behind other frames
+ * 2) multiple virtual interfaces couldn't be handled correctly
+ * 3) after-DTIM frames could get stuck behind other frames
+ *
+ * To solve this, hardware typically uses multiple different queues for all
+ * the different usages, and this needs to be propagated into mac80211 so it
+ * won't have the same problem with the software queues.
+ *
+ * Therefore, mac80211 now offers the %IEEE80211_HW_QUEUE_CONTROL capability
+ * flag that tells it that the driver implements its own queue control. To do
+ * so, the driver will set up the various queues in each &struct ieee80211_vif
+ * and the offchannel queue in &struct ieee80211_hw. In response, mac80211 will
+ * use those queue IDs in the hw_queue field of &struct ieee80211_tx_info and
+ * if necessary will queue the frame on the right software queue that mirrors
+ * the hardware queue.
+ * Additionally, the driver has to then use these HW queue IDs for the queue
+ * management functions (ieee80211_stop_queue() et al.)
+ *
+ * The driver is free to set up the queue mappings as needed, multiple virtual
+ * interfaces may map to the same hardware queues if needed. The setup has to
+ * happen during add_interface or change_interface callbacks. For example, a
+ * driver supporting station+station and station+AP modes might decide to have
+ * 10 hardware queues to handle different scenarios:
+ *
+ * 4 AC HW queues for 1st vif: 0, 1, 2, 3
+ * 4 AC HW queues for 2nd vif: 4, 5, 6, 7
+ * after-DTIM queue for AP: 8
+ * off-channel queue: 9
+ *
+ * It would then set up the hardware like this:
+ * hw.offchannel_tx_hw_queue = 9
+ *
+ * and the first virtual interface that is added as follows:
+ * vif.hw_queue[IEEE80211_AC_VO] = 0
+ * vif.hw_queue[IEEE80211_AC_VI] = 1
+ * vif.hw_queue[IEEE80211_AC_BE] = 2
+ * vif.hw_queue[IEEE80211_AC_BK] = 3
+ * vif.cab_queue = 8 // if AP mode, otherwise %IEEE80211_INVAL_HW_QUEUE
+ * and the second virtual interface with 4-7.
+ *
+ * If queue 6 gets full, for example, mac80211 would only stop the second
+ * virtual interface's BE queue since virtual interface queues are per AC.
+ *
+ * Note that the vif.cab_queue value should be set to %IEEE80211_INVAL_HW_QUEUE
+ * whenever the queue is not used (i.e. the interface is not in AP mode) if the
+ * queue could potentially be shared since mac80211 will look at cab_queue when
+ * a queue is stopped/woken even if the interface is not in AP mode.
+ */
+
/**
* enum ieee80211_filter_flags - hardware filter flags
*
IEEE80211_FRAME_RELEASE_UAPSD,
};
+/**
+ * enum ieee80211_rate_control_changed - flags to indicate what changed
+ *
+ * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit
+ * to this station changed.
+ * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed.
+ */
+enum ieee80211_rate_control_changed {
+ IEEE80211_RC_BW_CHANGED = BIT(0),
+ IEEE80211_RC_SMPS_CHANGED = BIT(1),
+};
+
/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
* up the list of states.
* The callback can sleep.
*
+ * @sta_rc_update: Notifies the driver of changes to the bitrates that can be
+ * used to transmit to the station. The changes are advertised with bits
+ * from &enum ieee80211_rate_control_changed and the values are reflected
+ * in the station data. This callback should only be used when the driver
+ * uses hardware rate control (%IEEE80211_HW_HAS_RATE_CONTROL) since
+ * otherwise the rate control algorithm is notified directly.
+ * Must be atomic.
+ *
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
* Returns a negative error code on failure.
#ifdef CONFIG_PM
int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
int (*resume)(struct ieee80211_hw *hw);
+ void (*set_wakeup)(struct ieee80211_hw *hw, bool enabled);
#endif
int (*add_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state);
+ void (*sta_rc_update)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 changed);
int (*conf_tx)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, u16 queue,
+ struct ieee80211_vif *vif, u16 ac,
const struct ieee80211_tx_queue_params *params);
u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
/* Rate control API */
-/**
- * enum rate_control_changed - flags to indicate which parameter changed
- *
- * @IEEE80211_RC_HT_CHANGED: The HT parameters of the operating channel have
- * changed, rate control algorithm can update its internal state if needed.
- * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed, the rate
- * control algorithm needs to adjust accordingly.
- */
-enum rate_control_changed {
- IEEE80211_RC_HT_CHANGED = BIT(0),
- IEEE80211_RC_SMPS_CHANGED = BIT(1),
-};
-
/**
* struct ieee80211_tx_rate_control - rate control information for/from RC algo
*
void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta);
void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta,
- void *priv_sta, u32 changed,
- enum nl80211_channel_type oper_chan_type);
+ struct ieee80211_sta *sta, void *priv_sta,
+ u32 changed);
void (*free_sta)(void *priv, struct ieee80211_sta *sta,
void *priv_sta);
void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif);
-int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb);
+int ieee80211_add_srates_ie(struct ieee80211_vif *vif,
+ struct sk_buff *skb, bool need_basic);
int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif,
- struct sk_buff *skb);
+ struct sk_buff *skb, bool need_basic);
#endif /* MAC80211_H */
static const struct file_operations name##_ops = { \
.read = ieee80211_if_read_##name, \
.write = (_write), \
- .open = mac80211_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
}
struct ieee80211_local *local = sdata->local;
unsigned long long tsf;
int ret;
+ int tsf_is_delta = 0;
if (strncmp(buf, "reset", 5) == 0) {
if (local->ops->reset_tsf) {
wiphy_info(local->hw.wiphy, "debugfs reset TSF\n");
}
} else {
+ if (buflen > 10 && buf[1] == '=') {
+ if (buf[0] == '+')
+ tsf_is_delta = 1;
+ else if (buf[0] == '-')
+ tsf_is_delta = -1;
+ else
+ return -EINVAL;
+ buf += 2;
+ }
ret = kstrtoull(buf, 10, &tsf);
if (ret < 0)
return -EINVAL;
+ if (tsf_is_delta)
+ tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
if (local->ops->set_tsf) {
drv_set_tsf(local, sdata, tsf);
wiphy_info(local->hw.wiphy,
IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC);
#endif
-
-#define DEBUGFS_ADD(name) \
- debugfs_create_file(#name, 0400, sdata->debugfs.dir, \
- sdata, &name##_ops);
-
#define DEBUGFS_ADD_MODE(name, mode) \
debugfs_create_file(#name, mode, sdata->debugfs.dir, \
sdata, &name##_ops);
-static void add_sta_files(struct ieee80211_sub_if_data *sdata)
+#define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400)
+
+static void add_common_files(struct ieee80211_sub_if_data *sdata)
{
DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+}
+static void add_sta_files(struct ieee80211_sub_if_data *sdata)
+{
DEBUGFS_ADD(bssid);
DEBUGFS_ADD(aid);
DEBUGFS_ADD(last_beacon);
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-
DEBUGFS_ADD(num_sta_authorized);
DEBUGFS_ADD(num_sta_ps);
DEBUGFS_ADD(dtim_count);
static void add_ibss_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-
DEBUGFS_ADD_MODE(tsf, 0600);
}
static void add_wds_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-
DEBUGFS_ADD(peer);
}
-static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-}
-
-static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
-}
-
#ifdef CONFIG_MAC80211_MESH
static void add_mesh_files(struct ieee80211_sub_if_data *sdata)
if (!sdata->debugfs.dir)
return;
+ DEBUGFS_ADD(flags);
+ DEBUGFS_ADD(state);
+ DEBUGFS_ADD(channel_type);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
+ add_common_files(sdata);
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_MESH_POINT:
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_WDS:
add_wds_files(sdata);
break;
- case NL80211_IFTYPE_MONITOR:
- add_monitor_files(sdata);
- break;
- case NL80211_IFTYPE_AP_VLAN:
- add_vlan_files(sdata);
- break;
default:
break;
}
#define STA_OPS(name) \
static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \
- .open = mac80211_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
}
static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \
.write = sta_##name##_write, \
- .open = mac80211_open_file_generic, \
+ .open = simple_open, \
.llseek = generic_file_llseek, \
}
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
- TEST(INSERTED), TEST(RATE_CONTROL));
+ TEST(INSERTED), TEST(RATE_CONTROL),
+ TEST(TOFFSET_KNOWN));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
return (1 << ecw) - 1;
}
-/*
- * ieee80211_enable_ht should be called only after the operating band
- * has been determined as ht configuration depends on the hw's
- * HT abilities for a specific band.
- */
-static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_ht_info *hti,
- const u8 *bssid, u16 ap_ht_cap_flags,
- bool beacon_htcap_ie)
+static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_operation *ht_oper,
+ const u8 *bssid, bool reconfig)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
u32 changed = 0;
- int hti_cfreq;
u16 ht_opmode;
- bool enable_ht = true;
- enum nl80211_channel_type prev_chantype;
- enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT;
- enum nl80211_channel_type tx_channel_type;
+ bool disable_40 = false;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- prev_chantype = sdata->vif.bss_conf.channel_type;
-
- hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan,
- sband->band);
- /* check that channel matches the right operating channel */
- if (local->hw.conf.channel->center_freq != hti_cfreq) {
- /* Some APs mess this up, evidently.
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel, for instance.
- */
- printk(KERN_DEBUG
- "%s: Wrong control channel in association"
- " response: configured center-freq: %d"
- " hti-cfreq: %d hti->control_chan: %d"
- " band: %d. Disabling HT.\n",
- sdata->name,
- local->hw.conf.channel->center_freq,
- hti_cfreq, hti->control_chan,
- sband->band);
- enable_ht = false;
- }
-
- if (enable_ht) {
- rx_channel_type = NL80211_CHAN_HT20;
-
- if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
- !ieee80111_cfg_override_disables_ht40(sdata) &&
- (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
- (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
- switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rx_channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rx_channel_type = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
+ switch (sdata->vif.bss_conf.channel_type) {
+ case NL80211_CHAN_HT40PLUS:
+ if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ disable_40 = true;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ disable_40 = true;
+ break;
+ default:
+ break;
}
- tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type);
-
- if (local->tmp_channel)
- local->tmp_channel_type = rx_channel_type;
+ /* This can change during the lifetime of the BSS */
+ if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
+ disable_40 = true;
- if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- rx_channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- rx_channel_type));
- }
-
- if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) {
- /*
- * Whenever the AP announces the HT mode change that can be
- * 40MHz intolerant or etc., it would be safer to stop tx
- * queues before doing hw config to avoid buffer overflow.
- */
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, bssid);
- /* flush out all packets */
- synchronize_net();
+ WARN_ON_ONCE(!sta);
- drv_flush(local, false);
- }
+ if (sta && !sta->supports_40mhz)
+ disable_40 = true;
- /* channel_type change automatically detected */
- ieee80211_hw_config(local, 0);
+ if (sta && (!reconfig ||
+ (disable_40 != !!(sta->sta.ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) {
- if (prev_chantype != tx_channel_type) {
- rcu_read_lock();
- sta = sta_info_get(sdata, bssid);
- if (sta)
- rate_control_rate_update(local, sband, sta,
- IEEE80211_RC_HT_CHANGED,
- tx_channel_type);
- rcu_read_unlock();
+ if (disable_40)
+ sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ else
+ sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- if (beacon_htcap_ie)
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+ rate_control_rate_update(local, sband, sta,
+ IEEE80211_RC_BW_CHANGED);
}
+ mutex_unlock(&local->sta_mtx);
- ht_opmode = le16_to_cpu(hti->operation_mode);
+ ht_opmode = le16_to_cpu(ht_oper->operation_mode);
/* if bss configuration changed store the new one */
- if (sdata->ht_opmode_valid != enable_ht ||
- sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
- prev_chantype != rx_channel_type) {
+ if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) {
changed |= BSS_CHANGED_HT;
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
- sdata->ht_opmode_valid = enable_ht;
}
return changed;
}
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, const u8 *ht_info_ie,
+ struct sk_buff *skb, const u8 *ht_oper_ie,
struct ieee80211_supported_band *sband,
struct ieee80211_channel *channel,
enum ieee80211_smps_mode smps)
{
- struct ieee80211_ht_info *ht_info;
+ struct ieee80211_ht_operation *ht_oper;
u8 *pos;
u32 flags = channel->flags;
u16 cap;
BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
- if (!ht_info_ie)
+ if (!ht_oper_ie)
return;
- if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
+ if (ht_oper_ie[1] < sizeof(struct ieee80211_ht_operation))
return;
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
- ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
+ ht_oper = (struct ieee80211_ht_operation *)(ht_oper_ie + 2);
/* determine capability flags */
cap = ht_cap.cap;
- switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
break;
}
+ /*
+ * If 40 MHz was disabled associate as though we weren't
+ * capable of 40 MHz -- some broken APs will never fall
+ * back to trying to transmit in 20 MHz.
+ */
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) {
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
+ }
+
/* set SM PS mode properly */
cap &= ~IEEE80211_HT_CAP_SM_PS;
switch (smps) {
}
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie,
+ ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_operation_ie,
sband, local->oper_channel, ifmgd->ap_smps);
/* if present, add any custom non-vendor IEs that go after HT */
if (!local->ops->conf_tx)
return;
- if (local->hw.queues < 4)
+ if (local->hw.queues < IEEE80211_NUM_ACS)
return;
if (!wmm_param)
sdata->vif.bss_conf.assoc = false;
/* on the next assoc, re-program HT parameters */
- sdata->ht_opmode_valid = false;
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
ifmgd->nullfunc_failed = false;
ieee80211_send_nullfunc(sdata->local, sdata, 0);
} else {
+ int ssid_len;
+
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
- (u32) -1, true, false);
+ if (WARN_ON_ONCE(ssid == NULL))
+ ssid_len = 0;
+ else
+ ssid_len = ssid[1];
+
+ ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+ 0, (u32) -1, true, false);
}
ifmgd->probe_send_count++;
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
run_again(ifmgd, ifmgd->probe_timeout);
+ if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ drv_flush(sdata->local, false);
}
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sk_buff *skb;
const u8 *ssid;
+ int ssid_len;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return NULL;
return NULL;
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+ if (WARN_ON_ONCE(ssid == NULL))
+ ssid_len = 0;
+ else
+ ssid_len = ssid[1];
+
skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
- (u32) -1, ssid + 2, ssid[1],
+ (u32) -1, ssid + 2, ssid_len,
NULL, 0, true);
return skb;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u32 changed = 0;
int err;
- u16 ap_ht_cap_flags;
/* AssocResp and ReassocResp have identical structure */
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
- ap_ht_cap_flags = sta->sta.ht_cap.cap;
+ sta->supports_40mhz =
+ sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
rate_control_rate_init(sta);
ieee80211_set_wmm_default(sdata, false);
changed |= BSS_CHANGED_QOS;
- if (elems.ht_info_elem && elems.wmm_param &&
+ if (elems.ht_operation && elems.wmm_param &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- cbss->bssid, ap_ht_cap_flags,
- false);
+ changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
+ cbss->bssid, false);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
(1ULL << WLAN_EID_HT_CAPABILITY) |
- (1ULL << WLAN_EID_HT_INFORMATION);
+ (1ULL << WLAN_EID_HT_OPERATION);
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
if (directed_tim) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
- local->hw.conf.flags &= ~IEEE80211_CONF_PS;
- ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_PS);
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
ieee80211_send_nullfunc(local, sdata, 0);
- } else {
+ } else if (!local->pspolling && sdata->u.mgd.powersave) {
local->pspolling = true;
/*
erp_valid, erp_value);
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
+ if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
- struct sta_info *sta;
struct ieee80211_supported_band *sband;
- u16 ap_ht_cap_flags;
-
- rcu_read_lock();
-
- sta = sta_info_get(sdata, bssid);
- if (WARN_ON(!sta)) {
- rcu_read_unlock();
- return;
- }
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
- elems.ht_cap_elem, &sta->sta.ht_cap);
-
- ap_ht_cap_flags = sta->sta.ht_cap.cap;
-
- rcu_read_unlock();
-
- changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- bssid, ap_ht_cap_flags, true);
+ changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
+ bssid, true);
}
/* Note: country IE parsing is done for us by cfg80211 */
struct sta_info *sta;
bool have_sta = false;
int err;
+ int ht_cfreq;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ const u8 *ht_oper_ie;
+ const struct ieee80211_ht_operation *ht_oper = NULL;
+ struct ieee80211_supported_band *sband;
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
return -EINVAL;
mutex_unlock(&local->mtx);
/* switch to the right channel */
+ sband = local->hw.wiphy->bands[cbss->channel->band];
+
+ ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
+
+ if (sband->ht_cap.ht_supported) {
+ ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
+ cbss->information_elements,
+ cbss->len_information_elements);
+ if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
+ ht_oper = (void *)(ht_oper_ie + 2);
+ }
+
+ if (ht_oper) {
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
+ cbss->channel->band);
+ /* check that channel matches the right operating channel */
+ if (cbss->channel->center_freq != ht_cfreq) {
+ /*
+ * It's possible that some APs are confused here;
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel in association responses, but
+ * since we look at probe response/beacon data here
+ * it should be OK.
+ */
+ printk(KERN_DEBUG
+ "%s: Wrong control channel: center-freq: %d"
+ " ht-cfreq: %d ht->primary_chan: %d"
+ " band: %d. Disabling HT.\n",
+ sdata->name, cbss->channel->center_freq,
+ ht_cfreq, ht_oper->primary_chan,
+ cbss->channel->band);
+ ht_oper = NULL;
+ }
+ }
+
+ if (ht_oper) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+ /* can only fail due to HT40+/- mismatch */
+ channel_type = NL80211_CHAN_HT20;
+ printk(KERN_DEBUG
+ "%s: disabling 40 MHz due to multi-vif mismatch\n",
+ sdata->name);
+ ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
+ WARN_ON(!ieee80211_set_channel_type(local, sdata,
+ channel_type));
+ }
+
local->oper_channel = cbss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_hw_config(local, 0);
if (!have_sta) {
- struct ieee80211_supported_band *sband;
u32 rates = 0, basic_rates = 0;
bool have_higher_than_11mbit;
int min_rate = INT_MAX, min_rate_index = -1;
- sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
-
ieee80211_get_rates(sband, bss->supp_rates,
bss->supp_rates_len,
&rates, &basic_rates,
/* Also disable HT if we don't support it or the AP doesn't use WMM */
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
- local->hw.queues < 4 || !bss->wmm_used)
+ local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used)
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
ifmgd->ap_smps = ifmgd->req_smps;
assoc_data->capability = req->bss->capability;
- assoc_data->wmm = bss->wmm_used && (local->hw.queues >= 4);
+ assoc_data->wmm = bss->wmm_used &&
+ (local->hw.queues >= IEEE80211_NUM_ACS);
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
- assoc_data->ht_information_ie =
- ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
+ assoc_data->ht_operation_ie =
+ ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
*/
printk(KERN_DEBUG "%s: waiting for beacon from %pM\n",
sdata->name, ifmgd->bssid);
- assoc_data->timeout = jiffies +
- TU_TO_EXP_TIME(req->bss->beacon_interval);
+ assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
} else {
assoc_data->have_beacon = true;
assoc_data->sent_assoc = false;
static int parse_txq_params(struct nlattr *tb[],
struct ieee80211_txq_params *txq_params)
{
- if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+ if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
!tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
!tb[NL80211_TXQ_ATTR_AIFS])
return -EINVAL;
- txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
+ txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+ if (txq_params->ac >= NL80211_NUM_ACS)
+ return -EINVAL;
+
return 0;
}
goto bad_res;
}
+ if (!netif_running(netdev)) {
+ result = -ENETDOWN;
+ goto bad_res;
+ }
+
nla_for_each_nested(nl_txq_params,
info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
rem_txq_params) {
NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS,
sizeof(struct nl80211_sta_flag_update),
&sinfo->sta_flags);
+ if (sinfo->filled & STATION_INFO_T_OFFSET)
+ NLA_PUT_U64(msg, NL80211_STA_INFO_T_OFFSET,
+ sinfo->t_offset);
nla_nest_end(msg, sinfoattr);
if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
cur_params.element_ttl);
NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
cur_params.auto_open_plinks);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ cur_params.dot11MeshNbrOffsetMaxNeighbor);
NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
cur_params.dot11MeshHWMPmaxPREQretries);
NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
[NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
[NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
[NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
[NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
[NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
static const struct nla_policy
nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
+ mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ nla_get_u32);
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
nla_get_u8);
nl80211_mesh_setup_params_policy))
return -EINVAL;
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
+ setup->sync_method =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
+ IEEE80211_SYNC_METHOD_VENDOR :
+ IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
+
if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
setup->path_sel_proto =
(nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
struct cfg80211_wowlan new_triggers = {};
struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
int err, i;
+ bool prev_enabled = rdev->wowlan;
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
return -EOPNOTSUPP;
rdev->wowlan = NULL;
}
+ if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
+ rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
+
return 0;
error:
for (i = 0; i < new_triggers.n_patterns; i++)
.doit = nl80211_get_key,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.doit = nl80211_set_beacon,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.doit = nl80211_start_ap,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.doit = nl80211_stop_ap,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_set_station,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_del_station,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_del_mpath,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_set_bss,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_get_mesh_config,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_setdel_pmksa,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_setdel_pmksa,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_flush_pmksa,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.doit = nl80211_probe_client,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
nlmsg_free(msg);
}
+void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int freq,
+ enum nl80211_channel_type type, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,