* drivers/net/wireless/mwl8k.c
* Driver for Marvell TOPDOG 802.11 Wireless cards
*
- * Copyright (C) 2008-2009 Marvell Semiconductor Inc.
+ * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/etherdevice.h>
+#include <linux/slab.h>
#include <net/mac80211.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver"
#define MWL8K_NAME KBUILD_MODNAME
-#define MWL8K_VERSION "0.11"
+#define MWL8K_VERSION "0.12"
/* Register definitions */
#define MWL8K_HIU_GEN_PTR 0x00000c10
/* sw appends here */
int tail;
- struct ieee80211_tx_queue_stats stats;
+ unsigned int len;
struct mwl8k_tx_desc *txd;
dma_addr_t txd_dma;
struct sk_buff **skb;
struct ieee80211_supported_band band_24;
struct ieee80211_channel channels_24[14];
struct ieee80211_rate rates_24[14];
+ struct ieee80211_supported_band band_50;
+ struct ieee80211_channel channels_50[4];
+ struct ieee80211_rate rates_50[9];
+ u32 ap_macids_supported;
+ u32 sta_macids_supported;
/* firmware access */
struct mutex fw_mutex;
/* TX quiesce completion, protected by fw_mutex and tx_lock */
struct completion *tx_wait;
- struct ieee80211_vif *vif;
+ /* List of interfaces. */
+ u32 macids_used;
+ struct list_head vif_list;
/* power management status cookie from firmware */
u32 *cookie;
bool sniffer_enabled;
bool wmm_enabled;
- struct work_struct sta_notify_worker;
- spinlock_t sta_notify_list_lock;
- struct list_head sta_notify_list;
-
/* XXX need to convert this to handle multiple interfaces */
bool capture_beacon;
u8 capture_bssid[ETH_ALEN];
/* Per interface specific private data */
struct mwl8k_vif {
+ struct list_head list;
+ struct ieee80211_vif *vif;
+
+ /* Firmware macid for this vif. */
+ int macid;
+
/* Non AMPDU sequence number assigned by driver. */
u16 seqno;
};
{ .bitrate = 720, .hw_value = 144, },
};
+static const struct ieee80211_channel mwl8k_channels_50[] = {
+ { .center_freq = 5180, .hw_value = 36, },
+ { .center_freq = 5200, .hw_value = 40, },
+ { .center_freq = 5220, .hw_value = 44, },
+ { .center_freq = 5240, .hw_value = 48, },
+};
+
+static const struct ieee80211_rate mwl8k_rates_50[] = {
+ { .bitrate = 60, .hw_value = 12, },
+ { .bitrate = 90, .hw_value = 18, },
+ { .bitrate = 120, .hw_value = 24, },
+ { .bitrate = 180, .hw_value = 36, },
+ { .bitrate = 240, .hw_value = 48, },
+ { .bitrate = 360, .hw_value = 72, },
+ { .bitrate = 480, .hw_value = 96, },
+ { .bitrate = 540, .hw_value = 108, },
+ { .bitrate = 720, .hw_value = 144, },
+};
+
/* Set or get info from Firmware */
#define MWL8K_CMD_SET 0x0001
#define MWL8K_CMD_GET 0x0000
#define MWL8K_CMD_RADIO_CONTROL 0x001c
#define MWL8K_CMD_RF_TX_POWER 0x001e
#define MWL8K_CMD_RF_ANTENNA 0x0020
-#define MWL8K_CMD_SET_BEACON 0x0100
+#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */
#define MWL8K_CMD_SET_PRE_SCAN 0x0107
#define MWL8K_CMD_SET_POST_SCAN 0x0108
#define MWL8K_CMD_SET_RF_CHANNEL 0x010a
#define MWL8K_CMD_MIMO_CONFIG 0x0125
#define MWL8K_CMD_USE_FIXED_RATE 0x0126
#define MWL8K_CMD_ENABLE_SNIFFER 0x0150
-#define MWL8K_CMD_SET_MAC_ADDR 0x0202
+#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */
#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
-#define MWL8K_CMD_BSS_START 0x1100
-#define MWL8K_CMD_SET_NEW_STN 0x1111
+#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */
+#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */
#define MWL8K_CMD_UPDATE_STADB 0x1123
static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
struct mwl8k_cmd_pkt {
__le16 code;
__le16 length;
- __le16 seq_num;
+ __u8 seq_num;
+ __u8 macid;
__le16 result;
char payload[0];
} __attribute__((packed));
cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD);
cmd->seq_num = 0;
+ cmd->macid = 0;
cmd->result = 0;
done = 0;
memset(status, 0, sizeof(*status));
status->signal = -rxd->rssi;
- status->noise = -rxd->noise_floor;
if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
status->flag |= RX_FLAG_HT;
}
}
- status->band = IEEE80211_BAND_2GHZ;
+ if (rxd->channel > 14) {
+ status->band = IEEE80211_BAND_5GHZ;
+ if (!(status->flag & RX_FLAG_HT))
+ status->rate_idx -= 5;
+ } else {
+ status->band = IEEE80211_BAND_2GHZ;
+ }
status->freq = ieee80211_channel_to_frequency(rxd->channel);
*qos = rxd->qos_control;
memset(status, 0, sizeof(*status));
status->signal = -rxd->rssi;
- status->noise = -rxd->noise_level;
status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
status->flag |= RX_FLAG_HT;
- status->band = IEEE80211_BAND_2GHZ;
+ if (rxd->channel > 14) {
+ status->band = IEEE80211_BAND_5GHZ;
+ if (!(status->flag & RX_FLAG_HT))
+ status->rate_idx -= 5;
+ } else {
+ status->band = IEEE80211_BAND_2GHZ;
+ }
status->freq = ieee80211_channel_to_frequency(rxd->channel);
*qos = rxd->qos_control;
int size;
int i;
- memset(&txq->stats, 0, sizeof(struct ieee80211_tx_queue_stats));
- txq->stats.limit = MWL8K_TX_DESCS;
+ txq->len = 0;
txq->head = 0;
txq->tail = 0;
printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d "
"fw_owned=%d drv_owned=%d unused=%d\n",
wiphy_name(hw->wiphy), i,
- txq->stats.len, txq->head, txq->tail,
+ txq->len, txq->head, txq->tail,
fw_owned, drv_owned, unused);
}
}
int processed;
processed = 0;
- while (txq->stats.len > 0 && limit--) {
+ while (txq->len > 0 && limit--) {
int tx;
struct mwl8k_tx_desc *tx_desc;
unsigned long addr;
}
txq->head = (tx + 1) % MWL8K_TX_DESCS;
- BUG_ON(txq->stats.len == 0);
- txq->stats.len--;
+ BUG_ON(txq->len == 0);
+ txq->len--;
priv->pending_tx_pkts--;
addr = le32_to_cpu(tx_desc->pkt_phys_addr);
wmb();
tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
- txq->stats.count++;
- txq->stats.len++;
+ txq->len++;
priv->pending_tx_pkts++;
txq->tail++;
return rc;
}
+static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct mwl8k_cmd_pkt *cmd)
+{
+ if (vif != NULL)
+ cmd->macid = MWL8K_VIF(vif)->macid;
+ return mwl8k_post_cmd(hw, cmd);
+}
+
+/*
+ * Setup code shared between STA and AP firmware images.
+ */
+static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw)
+{
+ struct mwl8k_priv *priv = hw->priv;
+
+ BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24));
+ memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
+
+ BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
+ memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
+
+ priv->band_24.band = IEEE80211_BAND_2GHZ;
+ priv->band_24.channels = priv->channels_24;
+ priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
+ priv->band_24.bitrates = priv->rates_24;
+ priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24;
+}
+
+static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw)
+{
+ struct mwl8k_priv *priv = hw->priv;
+
+ BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50));
+ memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50));
+
+ BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50));
+ memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50));
+
+ priv->band_50.band = IEEE80211_BAND_5GHZ;
+ priv->band_50.channels = priv->channels_50;
+ priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50);
+ priv->band_50.bitrates = priv->rates_50;
+ priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50);
+
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50;
+}
+
/*
* CMD_GET_HW_SPEC (STA version).
*/
#define MWL8K_CAP_DELAY_BA 0x00003000
#define MWL8K_CAP_MIMO 0x00000200
#define MWL8K_CAP_40MHZ 0x00000100
+#define MWL8K_CAP_BAND_MASK 0x00000007
+#define MWL8K_CAP_5GHZ 0x00000004
+#define MWL8K_CAP_2GHZ4 0x00000001
-static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, u32 cap)
+static void
+mwl8k_set_ht_caps(struct ieee80211_hw *hw,
+ struct ieee80211_supported_band *band, u32 cap)
{
- struct mwl8k_priv *priv = hw->priv;
- struct ieee80211_supported_band *band = &priv->band_24;
int rx_streams;
int tx_streams;
}
}
+static void
+mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
+{
+ struct mwl8k_priv *priv = hw->priv;
+
+ if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
+ mwl8k_setup_2ghz_band(hw);
+ if (caps & MWL8K_CAP_MIMO)
+ mwl8k_set_ht_caps(hw, &priv->band_24, caps);
+ }
+
+ if (caps & MWL8K_CAP_5GHZ) {
+ mwl8k_setup_5ghz_band(hw);
+ if (caps & MWL8K_CAP_MIMO)
+ mwl8k_set_ht_caps(hw, &priv->band_50, caps);
+ }
+}
+
static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
{
struct mwl8k_priv *priv = hw->priv;
priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
priv->fw_rev = le32_to_cpu(cmd->fw_rev);
priv->hw_rev = cmd->hw_rev;
- if (cmd->caps & cpu_to_le32(MWL8K_CAP_MIMO))
- mwl8k_set_ht_caps(hw, le32_to_cpu(cmd->caps));
+ mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
+ priv->ap_macids_supported = 0x00000000;
+ priv->sta_macids_supported = 0x00000001;
}
kfree(cmd);
priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
priv->fw_rev = le32_to_cpu(cmd->fw_rev);
priv->hw_rev = cmd->hw_rev;
+ mwl8k_setup_2ghz_band(hw);
+ priv->ap_macids_supported = 0x000000ff;
+ priv->sta_macids_supported = 0x00000000;
off = le32_to_cpu(cmd->wcbbase0) & 0xffff;
iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off);
static struct mwl8k_cmd_pkt *
__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
- int mc_count, struct dev_addr_list *mclist)
+ struct netdev_hw_addr_list *mc_list)
{
struct mwl8k_priv *priv = hw->priv;
struct mwl8k_cmd_mac_multicast_adr *cmd;
int size;
+ int mc_count = 0;
+
+ if (mc_list)
+ mc_count = netdev_hw_addr_list_count(mc_list);
if (allmulti || mc_count > priv->num_mcaddrs) {
allmulti = 1;
if (allmulti) {
cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
} else if (mc_count) {
- int i;
+ struct netdev_hw_addr *ha;
+ int i = 0;
cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
cmd->numaddr = cpu_to_le16(mc_count);
- for (i = 0; i < mc_count && mclist; i++) {
- if (mclist->da_addrlen != ETH_ALEN) {
- kfree(cmd);
- return NULL;
- }
- memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN);
- mclist = mclist->next;
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ memcpy(cmd->addr[i], ha->addr, ETH_ALEN);
}
}
__u8 beacon[0];
};
-static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, u8 *beacon, int len)
+static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *beacon, int len)
{
struct mwl8k_cmd_set_beacon *cmd;
int rc;
cmd->beacon_len = cpu_to_le16(len);
memcpy(cmd->beacon, beacon, len);
- rc = mwl8k_post_cmd(hw, &cmd->header);
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
return rc;
if (channel->band == IEEE80211_BAND_2GHZ)
cmd->channel_flags |= cpu_to_le32(0x00000001);
+ else if (channel->band == IEEE80211_BAND_5GHZ)
+ cmd->channel_flags |= cpu_to_le32(0x00000004);
if (conf->channel_type == NL80211_CHAN_NO_HT ||
conf->channel_type == NL80211_CHAN_HT20)
};
} __attribute__((packed));
-#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0
-#define MWL8K_MAC_TYPE_PRIMARY_AP 2
+#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0
+#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1
+#define MWL8K_MAC_TYPE_PRIMARY_AP 2
+#define MWL8K_MAC_TYPE_SECONDARY_AP 3
-static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac)
+static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, u8 *mac)
{
struct mwl8k_priv *priv = hw->priv;
+ struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
struct mwl8k_cmd_set_mac_addr *cmd;
+ int mac_type;
int rc;
+ mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
+ if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
+ if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
+ mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
+ else
+ mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
+ } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
+ if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported))
+ mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
+ else
+ mac_type = MWL8K_MAC_TYPE_SECONDARY_AP;
+ }
+
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
cmd->header.length = cpu_to_le16(sizeof(*cmd));
if (priv->ap_fw) {
- cmd->mbss.mac_type = cpu_to_le16(MWL8K_MAC_TYPE_PRIMARY_AP);
+ cmd->mbss.mac_type = cpu_to_le16(mac_type);
memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
} else {
memcpy(cmd->mac_addr, mac, ETH_ALEN);
}
- rc = mwl8k_post_cmd(hw, &cmd->header);
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
return rc;
__le32 enable;
} __attribute__((packed));
-static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, int enable)
+static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int enable)
{
struct mwl8k_cmd_bss_start *cmd;
int rc;
cmd->header.length = cpu_to_le16(sizeof(*cmd));
cmd->enable = cpu_to_le32(enable);
- rc = mwl8k_post_cmd(hw, &cmd->header);
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
return rc;
struct ieee80211_sta *sta)
{
struct mwl8k_cmd_set_new_stn *cmd;
+ u32 rates;
int rc;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);
cmd->stn_id = cpu_to_le16(sta->aid);
cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD);
- cmd->legacy_rates = cpu_to_le32(sta->supp_rates[IEEE80211_BAND_2GHZ]);
+ if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+ rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
+ else
+ rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
+ cmd->legacy_rates = cpu_to_le32(rates);
if (sta->ht_cap.ht_supported) {
cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
cmd->is_qos_sta = 1;
}
- rc = mwl8k_post_cmd(hw, &cmd->header);
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
return rc;
cmd->header.length = cpu_to_le16(sizeof(*cmd));
memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
- rc = mwl8k_post_cmd(hw, &cmd->header);
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
return rc;
memcpy(cmd->mac_addr, addr, ETH_ALEN);
cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE);
- rc = mwl8k_post_cmd(hw, &cmd->header);
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
return rc;
{
struct mwl8k_cmd_update_stadb *cmd;
struct peer_capability_info *p;
+ u32 rates;
int rc;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
p->ht_caps = sta->ht_cap.cap;
p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |
((sta->ht_cap.ampdu_density & 7) << 2);
- legacy_rate_mask_to_array(p->legacy_rates,
- sta->supp_rates[IEEE80211_BAND_2GHZ]);
+ if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+ rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
+ else
+ rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
+ legacy_rate_mask_to_array(p->legacy_rates, rates);
memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16);
p->interop = 1;
p->amsdu_enabled = 0;
}
static int mwl8k_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+ struct ieee80211_vif *vif)
{
struct mwl8k_priv *priv = hw->priv;
struct mwl8k_vif *mwl8k_vif;
-
- /*
- * We only support one active interface at a time.
- */
- if (priv->vif != NULL)
- return -EBUSY;
+ u32 macids_supported;
+ int macid;
/*
* Reject interface creation if sniffer mode is active, as
return -EINVAL;
}
- /* Set the mac address. */
- mwl8k_cmd_set_mac_addr(hw, vif->addr);
- if (priv->ap_fw)
- mwl8k_cmd_set_new_stn_add_self(hw, vif);
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ macids_supported = priv->ap_macids_supported;
+ break;
+ case NL80211_IFTYPE_STATION:
+ macids_supported = priv->sta_macids_supported;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ macid = ffs(macids_supported & ~priv->macids_used);
+ if (!macid--)
+ return -EBUSY;
- /* Clean out driver private area */
+ /* Setup driver private area. */
mwl8k_vif = MWL8K_VIF(vif);
memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
-
- /* Set Initial sequence number to zero */
+ mwl8k_vif->vif = vif;
+ mwl8k_vif->macid = macid;
mwl8k_vif->seqno = 0;
- priv->vif = vif;
+ /* Set the mac address. */
+ mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
+
+ if (priv->ap_fw)
+ mwl8k_cmd_set_new_stn_add_self(hw, vif);
+
+ priv->macids_used |= 1 << mwl8k_vif->macid;
+ list_add_tail(&mwl8k_vif->list, &priv->vif_list);
return 0;
}
struct ieee80211_vif *vif)
{
struct mwl8k_priv *priv = hw->priv;
+ struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
if (priv->ap_fw)
mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
- mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
+ mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00");
- priv->vif = NULL;
+ priv->macids_used &= ~(1 << mwl8k_vif->macid);
+ list_del(&mwl8k_vif->list);
}
static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
goto out;
}
- ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];
+ if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+ ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];
+ } else {
+ ap_legacy_rates =
+ ap->supp_rates[IEEE80211_BAND_5GHZ] << 5;
+ }
memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16);
rcu_read_unlock();
* beacons will always go out at 1 Mb/s).
*/
idx = ffs(vif->bss_conf.basic_rates);
- rate = idx ? mwl8k_rates_24[idx - 1].hw_value : 2;
+ if (idx)
+ idx--;
+
+ if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+ rate = mwl8k_rates_24[idx].hw_value;
+ else
+ rate = mwl8k_rates_50[idx].hw_value;
mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
}
skb = ieee80211_beacon_get(hw, vif);
if (skb != NULL) {
- mwl8k_cmd_set_beacon(hw, skb->data, skb->len);
+ mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
kfree_skb(skb);
}
}
if (changed & BSS_CHANGED_BEACON_ENABLED)
- mwl8k_cmd_bss_start(hw, info->enable_beacon);
+ mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
out:
mwl8k_fw_unlock(hw);
}
static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
- int mc_count, struct dev_addr_list *mclist)
+ struct netdev_hw_addr_list *mc_list)
{
struct mwl8k_cmd_pkt *cmd;
* we'll end up throwing this packet away and creating a new
* one in mwl8k_configure_filter().
*/
- cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_count, mclist);
+ cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list);
return (unsigned long)cmd;
}
* operation, so refuse to enable sniffer mode if a STA
* interface is active.
*/
- if (priv->vif != NULL) {
+ if (!list_empty(&priv->vif_list)) {
if (net_ratelimit())
printk(KERN_INFO "%s: not enabling sniffer "
"mode because STA interface is active\n",
return 1;
}
+static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv)
+{
+ if (!list_empty(&priv->vif_list))
+ return list_entry(priv->vif_list.next, struct mwl8k_vif, list);
+
+ return NULL;
+}
+
static void mwl8k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
*/
mwl8k_cmd_set_pre_scan(hw);
} else {
+ struct mwl8k_vif *mwl8k_vif;
const u8 *bssid;
/*
* (where the OUI part needs to be nonzero for
* the BSSID to be accepted by POST_SCAN).
*/
- bssid = "\x01\x00\x00\x00\x00\x00";
- if (priv->vif != NULL)
- bssid = priv->vif->bss_conf.bssid;
+ mwl8k_vif = mwl8k_first_vif(priv);
+ if (mwl8k_vif != NULL)
+ bssid = mwl8k_vif->vif->bss_conf.bssid;
+ else
+ bssid = "\x01\x00\x00\x00\x00\x00";
mwl8k_cmd_set_post_scan(hw, bssid);
}
*/
if (*total_flags & FIF_ALLMULTI) {
kfree(cmd);
- cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, 0, NULL);
+ cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL);
}
if (cmd != NULL) {
return mwl8k_cmd_set_rts_threshold(hw, value);
}
-struct mwl8k_sta_notify_item
-{
- struct list_head list;
- struct ieee80211_vif *vif;
- enum sta_notify_cmd cmd;
- struct ieee80211_sta sta;
-};
-
-static void
-mwl8k_do_sta_notify(struct ieee80211_hw *hw, struct mwl8k_sta_notify_item *s)
+static int mwl8k_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
struct mwl8k_priv *priv = hw->priv;
- /*
- * STA firmware uses UPDATE_STADB, AP firmware uses SET_NEW_STN.
- */
- if (!priv->ap_fw && s->cmd == STA_NOTIFY_ADD) {
- int rc;
-
- rc = mwl8k_cmd_update_stadb_add(hw, s->vif, &s->sta);
- if (rc >= 0) {
- struct ieee80211_sta *sta;
-
- rcu_read_lock();
- sta = ieee80211_find_sta(s->vif, s->sta.addr);
- if (sta != NULL)
- MWL8K_STA(sta)->peer_id = rc;
- rcu_read_unlock();
- }
- } else if (!priv->ap_fw && s->cmd == STA_NOTIFY_REMOVE) {
- mwl8k_cmd_update_stadb_del(hw, s->vif, s->sta.addr);
- } else if (priv->ap_fw && s->cmd == STA_NOTIFY_ADD) {
- mwl8k_cmd_set_new_stn_add(hw, s->vif, &s->sta);
- } else if (priv->ap_fw && s->cmd == STA_NOTIFY_REMOVE) {
- mwl8k_cmd_set_new_stn_del(hw, s->vif, s->sta.addr);
- }
-}
-
-static void mwl8k_sta_notify_worker(struct work_struct *work)
-{
- struct mwl8k_priv *priv =
- container_of(work, struct mwl8k_priv, sta_notify_worker);
- struct ieee80211_hw *hw = priv->hw;
-
- spin_lock_bh(&priv->sta_notify_list_lock);
- while (!list_empty(&priv->sta_notify_list)) {
- struct mwl8k_sta_notify_item *s;
-
- s = list_entry(priv->sta_notify_list.next,
- struct mwl8k_sta_notify_item, list);
- list_del(&s->list);
-
- spin_unlock_bh(&priv->sta_notify_list_lock);
-
- mwl8k_do_sta_notify(hw, s);
- kfree(s);
-
- spin_lock_bh(&priv->sta_notify_list_lock);
- }
- spin_unlock_bh(&priv->sta_notify_list_lock);
+ if (priv->ap_fw)
+ return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr);
+ else
+ return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr);
}
-static void
-mwl8k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+static int mwl8k_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
struct mwl8k_priv *priv = hw->priv;
- struct mwl8k_sta_notify_item *s;
+ int ret;
- if (cmd != STA_NOTIFY_ADD && cmd != STA_NOTIFY_REMOVE)
- return;
-
- s = kmalloc(sizeof(*s), GFP_ATOMIC);
- if (s != NULL) {
- s->vif = vif;
- s->cmd = cmd;
- s->sta = *sta;
-
- spin_lock(&priv->sta_notify_list_lock);
- list_add_tail(&s->list, &priv->sta_notify_list);
- spin_unlock(&priv->sta_notify_list_lock);
+ if (!priv->ap_fw) {
+ ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
+ if (ret >= 0) {
+ MWL8K_STA(sta)->peer_id = ret;
+ return 0;
+ }
- ieee80211_queue_work(hw, &priv->sta_notify_worker);
+ return ret;
}
+
+ return mwl8k_cmd_set_new_stn_add(hw, vif, sta);
}
static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
return rc;
}
-static int mwl8k_get_tx_stats(struct ieee80211_hw *hw,
- struct ieee80211_tx_queue_stats *stats)
-{
- struct mwl8k_priv *priv = hw->priv;
- struct mwl8k_tx_queue *txq;
- int index;
-
- spin_lock_bh(&priv->tx_lock);
- for (index = 0; index < MWL8K_TX_QUEUES; index++) {
- txq = priv->txq + index;
- memcpy(&stats[index], &txq->stats,
- sizeof(struct ieee80211_tx_queue_stats));
- }
- spin_unlock_bh(&priv->tx_lock);
-
- return 0;
-}
-
static int mwl8k_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
.prepare_multicast = mwl8k_prepare_multicast,
.configure_filter = mwl8k_configure_filter,
.set_rts_threshold = mwl8k_set_rts_threshold,
- .sta_notify = mwl8k_sta_notify,
+ .sta_add = mwl8k_sta_add,
+ .sta_remove = mwl8k_sta_remove,
.conf_tx = mwl8k_conf_tx,
- .get_tx_stats = mwl8k_get_tx_stats,
.get_stats = mwl8k_get_stats,
.ampdu_action = mwl8k_ampdu_action,
};
struct mwl8k_priv *priv =
container_of(work, struct mwl8k_priv, finalize_join_worker);
struct sk_buff *skb = priv->beacon_skb;
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
+ mgmt->u.beacon.variable, len);
+ int dtim_period = 1;
- mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len,
- priv->vif->bss_conf.dtim_period);
- dev_kfree_skb(skb);
+ if (tim && tim[1] >= 2)
+ dtim_period = tim[3];
+
+ mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
+ dev_kfree_skb(skb);
priv->beacon_skb = NULL;
}
MODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
+ { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
{ PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
{ PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
{ PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
priv->pending_tx_pkts = 0;
- memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
- priv->band_24.band = IEEE80211_BAND_2GHZ;
- priv->band_24.channels = priv->channels_24;
- priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
- priv->band_24.bitrates = priv->rates_24;
- priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24;
-
- BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
- memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
-
/*
* Extra headroom is the size of the required DMA header
* minus the size of the smallest 802.11 frame (CTS frame).
hw->queues = MWL8K_TX_QUEUES;
- /* Set rssi and noise values to dBm */
- hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM;
+ /* Set rssi values to dBm */
+ hw->flags |= IEEE80211_HW_SIGNAL_DBM;
hw->vif_data_size = sizeof(struct mwl8k_vif);
hw->sta_data_size = sizeof(struct mwl8k_sta);
- priv->vif = NULL;
+
+ priv->macids_used = 0;
+ INIT_LIST_HEAD(&priv->vif_list);
/* Set default radio state and preamble */
priv->radio_on = 0;
priv->radio_short_preamble = 0;
- /* Station database handling */
- INIT_WORK(&priv->sta_notify_worker, mwl8k_sta_notify_worker);
- spin_lock_init(&priv->sta_notify_list_lock);
- INIT_LIST_HEAD(&priv->sta_notify_list);
-
/* Finalize join worker */
INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
rc = mwl8k_cmd_get_hw_spec_ap(hw);
if (!rc)
rc = mwl8k_cmd_set_hw_spec(hw);
-
- hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP);
} else {
rc = mwl8k_cmd_get_hw_spec_sta(hw);
-
- hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
}
if (rc) {
printk(KERN_ERR "%s: Cannot initialise firmware\n",
goto err_free_irq;
}
+ hw->wiphy->interface_modes = 0;
+ if (priv->ap_macids_supported)
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
+ if (priv->sta_macids_supported)
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
+
+
/* Turn radio off */
rc = mwl8k_cmd_radio_disable(hw);
if (rc) {
}
/* Clear MAC address */
- rc = mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
+ rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00");
if (rc) {
printk(KERN_ERR "%s: Cannot clear MAC address\n",
wiphy_name(hw->wiphy));