Commit b64fe619 authored by Lennert Buytenhek's avatar Lennert Buytenhek Committed by John W. Linville

mwl8k: basic AP interface support

Add support for creating AP interfaces, and enabling beaconing.
This allows running a basic AP (11b/g mode only for now).
Signed-off-by: default avatarLennert Buytenhek <buytenh@marvell.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a9e00b15
...@@ -266,6 +266,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { ...@@ -266,6 +266,7 @@ static const struct ieee80211_rate mwl8k_rates[] = {
#define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RADIO_CONTROL 0x001c
#define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_RF_TX_POWER 0x001e
#define MWL8K_CMD_RF_ANTENNA 0x0020 #define MWL8K_CMD_RF_ANTENNA 0x0020
#define MWL8K_CMD_SET_BEACON 0x0100
#define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_PRE_SCAN 0x0107
#define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_POST_SCAN 0x0108
#define MWL8K_CMD_SET_RF_CHANNEL 0x010a #define MWL8K_CMD_SET_RF_CHANNEL 0x010a
...@@ -281,6 +282,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { ...@@ -281,6 +282,7 @@ static const struct ieee80211_rate mwl8k_rates[] = {
#define MWL8K_CMD_ENABLE_SNIFFER 0x0150 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150
#define MWL8K_CMD_SET_MAC_ADDR 0x0202 #define MWL8K_CMD_SET_MAC_ADDR 0x0202
#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
#define MWL8K_CMD_BSS_START 0x1100
#define MWL8K_CMD_SET_NEW_STN 0x1111 #define MWL8K_CMD_SET_NEW_STN 0x1111
#define MWL8K_CMD_UPDATE_STADB 0x1123 #define MWL8K_CMD_UPDATE_STADB 0x1123
...@@ -299,6 +301,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) ...@@ -299,6 +301,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
MWL8K_CMDNAME(RADIO_CONTROL); MWL8K_CMDNAME(RADIO_CONTROL);
MWL8K_CMDNAME(RF_TX_POWER); MWL8K_CMDNAME(RF_TX_POWER);
MWL8K_CMDNAME(RF_ANTENNA); MWL8K_CMDNAME(RF_ANTENNA);
MWL8K_CMDNAME(SET_BEACON);
MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_PRE_SCAN);
MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_POST_SCAN);
MWL8K_CMDNAME(SET_RF_CHANNEL); MWL8K_CMDNAME(SET_RF_CHANNEL);
...@@ -314,6 +317,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) ...@@ -314,6 +317,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
MWL8K_CMDNAME(ENABLE_SNIFFER); MWL8K_CMDNAME(ENABLE_SNIFFER);
MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_MAC_ADDR);
MWL8K_CMDNAME(SET_RATEADAPT_MODE); MWL8K_CMDNAME(SET_RATEADAPT_MODE);
MWL8K_CMDNAME(BSS_START);
MWL8K_CMDNAME(SET_NEW_STN); MWL8K_CMDNAME(SET_NEW_STN);
MWL8K_CMDNAME(UPDATE_STADB); MWL8K_CMDNAME(UPDATE_STADB);
default: default:
...@@ -1769,7 +1773,9 @@ struct mwl8k_cmd_set_hw_spec { ...@@ -1769,7 +1773,9 @@ struct mwl8k_cmd_set_hw_spec {
__le32 total_rxd; __le32 total_rxd;
} __attribute__((packed)); } __attribute__((packed));
#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 #define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080
#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020
#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010
static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
{ {
...@@ -1790,7 +1796,9 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) ...@@ -1790,7 +1796,9 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
for (i = 0; i < MWL8K_TX_QUEUES; i++) for (i = 0; i < MWL8K_TX_QUEUES; i++)
cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT); cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON);
cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
...@@ -2027,6 +2035,35 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) ...@@ -2027,6 +2035,35 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
return rc; return rc;
} }
/*
* CMD_SET_BEACON.
*/
struct mwl8k_cmd_set_beacon {
struct mwl8k_cmd_pkt header;
__le16 beacon_len;
__u8 beacon[0];
};
static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, u8 *beacon, int len)
{
struct mwl8k_cmd_set_beacon *cmd;
int rc;
cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
cmd->beacon_len = cpu_to_le16(len);
memcpy(cmd->beacon, beacon, len);
rc = mwl8k_post_cmd(hw, &cmd->header);
kfree(cmd);
return rc;
}
/* /*
* CMD_SET_PRE_SCAN. * CMD_SET_PRE_SCAN.
*/ */
...@@ -2664,6 +2701,33 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) ...@@ -2664,6 +2701,33 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
return rc; return rc;
} }
/*
* CMD_BSS_START.
*/
struct mwl8k_cmd_bss_start {
struct mwl8k_cmd_pkt header;
__le32 enable;
} __attribute__((packed));
static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, int enable)
{
struct mwl8k_cmd_bss_start *cmd;
int rc;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START);
cmd->header.length = cpu_to_le16(sizeof(*cmd));
cmd->enable = cpu_to_le32(enable);
rc = mwl8k_post_cmd(hw, &cmd->header);
kfree(cmd);
return rc;
}
/* /*
* CMD_SET_NEW_STN. * CMD_SET_NEW_STN.
*/ */
...@@ -2727,6 +2791,26 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, ...@@ -2727,6 +2791,26 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
return rc; return rc;
} }
static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mwl8k_cmd_set_new_stn *cmd;
int rc;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
cmd->header.length = cpu_to_le16(sizeof(*cmd));
memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
rc = mwl8k_post_cmd(hw, &cmd->header);
kfree(cmd);
return rc;
}
static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u8 *addr) struct ieee80211_vif *vif, u8 *addr)
{ {
...@@ -3015,16 +3099,10 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, ...@@ -3015,16 +3099,10 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
if (priv->vif != NULL) if (priv->vif != NULL)
return -EBUSY; return -EBUSY;
/*
* We only support managed interfaces for now.
*/
if (vif->type != NL80211_IFTYPE_STATION)
return -EINVAL;
/* /*
* Reject interface creation if sniffer mode is active, as * Reject interface creation if sniffer mode is active, as
* STA operation is mutually exclusive with hardware sniffer * STA operation is mutually exclusive with hardware sniffer
* mode. * mode. (Sniffer mode is only used on STA firmware.)
*/ */
if (priv->sniffer_enabled) { if (priv->sniffer_enabled) {
printk(KERN_INFO "%s: unable to create STA " printk(KERN_INFO "%s: unable to create STA "
...@@ -3036,6 +3114,9 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, ...@@ -3036,6 +3114,9 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
/* Set the mac address. */ /* Set the mac address. */
mwl8k_cmd_set_mac_addr(hw, vif->addr); mwl8k_cmd_set_mac_addr(hw, vif->addr);
if (priv->ap_fw)
mwl8k_cmd_set_new_stn_add_self(hw, vif);
/* Clean out driver private area */ /* Clean out driver private area */
mwl8k_vif = MWL8K_VIF(vif); mwl8k_vif = MWL8K_VIF(vif);
memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
...@@ -3054,6 +3135,9 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, ...@@ -3054,6 +3135,9 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
{ {
struct mwl8k_priv *priv = hw->priv; struct mwl8k_priv *priv = hw->priv;
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, "\x00\x00\x00\x00\x00\x00");
priv->vif = NULL; priv->vif = NULL;
...@@ -3105,10 +3189,9 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) ...@@ -3105,10 +3189,9 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
return rc; return rc;
} }
static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, static void
struct ieee80211_vif *vif, mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info, struct ieee80211_bss_conf *info, u32 changed)
u32 changed)
{ {
struct mwl8k_priv *priv = hw->priv; struct mwl8k_priv *priv = hw->priv;
u32 ap_legacy_rates; u32 ap_legacy_rates;
...@@ -3188,6 +3271,66 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, ...@@ -3188,6 +3271,66 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw,
mwl8k_fw_unlock(hw); mwl8k_fw_unlock(hw);
} }
static void
mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info, u32 changed)
{
int rc;
if (mwl8k_fw_lock(hw))
return;
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
rc = mwl8k_set_radio_preamble(hw,
vif->bss_conf.use_short_preamble);
if (rc)
goto out;
}
if (changed & BSS_CHANGED_BASIC_RATES) {
int idx;
int rate;
/*
* Use lowest supported basic rate for multicasts
* and management frames (such as probe responses --
* beacons will always go out at 1 Mb/s).
*/
idx = ffs(vif->bss_conf.basic_rates);
rate = idx ? mwl8k_rates[idx - 1].hw_value : 2;
mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
}
if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
struct sk_buff *skb;
skb = ieee80211_beacon_get(hw, vif);
if (skb != NULL) {
mwl8k_cmd_set_beacon(hw, skb->data, skb->len);
kfree_skb(skb);
}
}
if (changed & BSS_CHANGED_BEACON_ENABLED)
mwl8k_cmd_bss_start(hw, info->enable_beacon);
out:
mwl8k_fw_unlock(hw);
}
static void
mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info, u32 changed)
{
struct mwl8k_priv *priv = hw->priv;
if (!priv->ap_fw)
mwl8k_bss_info_changed_sta(hw, vif, info, changed);
else
mwl8k_bss_info_changed_ap(hw, vif, info, changed);
}
static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
int mc_count, struct dev_addr_list *mclist) int mc_count, struct dev_addr_list *mclist)
{ {
...@@ -3766,6 +3909,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, ...@@ -3766,6 +3909,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
rc = mwl8k_cmd_get_hw_spec_ap(hw); rc = mwl8k_cmd_get_hw_spec_ap(hw);
if (!rc) if (!rc)
rc = mwl8k_cmd_set_hw_spec(hw); rc = mwl8k_cmd_set_hw_spec(hw);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP);
} else { } else {
rc = mwl8k_cmd_get_hw_spec_sta(hw); rc = mwl8k_cmd_get_hw_spec_sta(hw);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment