mwl8k: basic AP interface support
authorLennert Buytenhek <buytenh@wantstofly.org>
Fri, 8 Jan 2010 17:31:39 +0000 (18:31 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 12 Jan 2010 19:02:10 +0000 (14:02 -0500)
Add support for creating AP interfaces, and enabling beaconing.
This allows running a basic AP (11b/g mode only for now).

Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwl8k.c

index 428575beb8d08585bf47701973b056f6408603c8..759c94fb8e77cd5fdad155eaf77cabbd66847055 100644 (file)
@@ -266,6 +266,7 @@ static const struct ieee80211_rate mwl8k_rates[] = {
 #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_PRE_SCAN         0x0107
 #define MWL8K_CMD_SET_POST_SCAN                0x0108
 #define MWL8K_CMD_SET_RF_CHANNEL       0x010a
@@ -281,6 +282,7 @@ static const struct ieee80211_rate mwl8k_rates[] = {
 #define MWL8K_CMD_ENABLE_SNIFFER       0x0150
 #define MWL8K_CMD_SET_MAC_ADDR         0x0202
 #define MWL8K_CMD_SET_RATEADAPT_MODE   0x0203
+#define MWL8K_CMD_BSS_START            0x1100
 #define MWL8K_CMD_SET_NEW_STN          0x1111
 #define MWL8K_CMD_UPDATE_STADB         0x1123
 
@@ -299,6 +301,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
                MWL8K_CMDNAME(RADIO_CONTROL);
                MWL8K_CMDNAME(RF_TX_POWER);
                MWL8K_CMDNAME(RF_ANTENNA);
+               MWL8K_CMDNAME(SET_BEACON);
                MWL8K_CMDNAME(SET_PRE_SCAN);
                MWL8K_CMDNAME(SET_POST_SCAN);
                MWL8K_CMDNAME(SET_RF_CHANNEL);
@@ -314,6 +317,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
                MWL8K_CMDNAME(ENABLE_SNIFFER);
                MWL8K_CMDNAME(SET_MAC_ADDR);
                MWL8K_CMDNAME(SET_RATEADAPT_MODE);
+               MWL8K_CMDNAME(BSS_START);
                MWL8K_CMDNAME(SET_NEW_STN);
                MWL8K_CMDNAME(UPDATE_STADB);
        default:
@@ -1769,7 +1773,9 @@ struct mwl8k_cmd_set_hw_spec {
        __le32 total_rxd;
 } __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)
 {
@@ -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);
        for (i = 0; i < MWL8K_TX_QUEUES; i++)
                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->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
 
@@ -2027,6 +2035,35 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
        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.
  */
@@ -2664,6 +2701,33 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
        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.
  */
@@ -2727,6 +2791,26 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
        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,
                                     struct ieee80211_vif *vif, u8 *addr)
 {
@@ -3015,16 +3099,10 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        if (priv->vif != NULL)
                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
         * STA operation is mutually exclusive with hardware sniffer
-        * mode.
+        * mode.  (Sniffer mode is only used on STA firmware.)
         */
        if (priv->sniffer_enabled) {
                printk(KERN_INFO "%s: unable to create STA "
@@ -3036,6 +3114,9 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        /* 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);
+
        /* Clean out driver private area */
        mwl8k_vif = MWL8K_VIF(vif);
        memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
@@ -3054,6 +3135,9 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
 {
        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");
 
        priv->vif = NULL;
@@ -3105,10 +3189,9 @@ out:
        return rc;
 }
 
-static void mwl8k_bss_info_changed(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif,
-                                  struct ieee80211_bss_conf *info,
-                                  u32 changed)
+static void
+mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          struct ieee80211_bss_conf *info, u32 changed)
 {
        struct mwl8k_priv *priv = hw->priv;
        u32 ap_legacy_rates;
@@ -3188,6 +3271,66 @@ out:
        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,
                                   int mc_count, struct dev_addr_list *mclist)
 {
@@ -3766,6 +3909,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
                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);