diff options
author | Lennert Buytenhek <buytenh@wantstofly.org> | 2010-01-08 12:31:39 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-01-12 14:02:10 -0500 |
commit | b64fe619e371fc17d8d686d6d44aef1b41317880 (patch) | |
tree | 726411e61c6c80316c85e4a9273739eeeda51774 /drivers/net/wireless/mwl8k.c | |
parent | a9e00b151ec2121b7ae09d84a2b5a68b6461e98a (diff) |
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: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 171 |
1 files changed, 158 insertions, 13 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 428575beb8d0..759c94fb8e77 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
@@ -266,6 +266,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { | |||
266 | #define MWL8K_CMD_RADIO_CONTROL 0x001c | 266 | #define MWL8K_CMD_RADIO_CONTROL 0x001c |
267 | #define MWL8K_CMD_RF_TX_POWER 0x001e | 267 | #define MWL8K_CMD_RF_TX_POWER 0x001e |
268 | #define MWL8K_CMD_RF_ANTENNA 0x0020 | 268 | #define MWL8K_CMD_RF_ANTENNA 0x0020 |
269 | #define MWL8K_CMD_SET_BEACON 0x0100 | ||
269 | #define MWL8K_CMD_SET_PRE_SCAN 0x0107 | 270 | #define MWL8K_CMD_SET_PRE_SCAN 0x0107 |
270 | #define MWL8K_CMD_SET_POST_SCAN 0x0108 | 271 | #define MWL8K_CMD_SET_POST_SCAN 0x0108 |
271 | #define MWL8K_CMD_SET_RF_CHANNEL 0x010a | 272 | #define MWL8K_CMD_SET_RF_CHANNEL 0x010a |
@@ -281,6 +282,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { | |||
281 | #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 | 282 | #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 |
282 | #define MWL8K_CMD_SET_MAC_ADDR 0x0202 | 283 | #define MWL8K_CMD_SET_MAC_ADDR 0x0202 |
283 | #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 | 284 | #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 |
285 | #define MWL8K_CMD_BSS_START 0x1100 | ||
284 | #define MWL8K_CMD_SET_NEW_STN 0x1111 | 286 | #define MWL8K_CMD_SET_NEW_STN 0x1111 |
285 | #define MWL8K_CMD_UPDATE_STADB 0x1123 | 287 | #define MWL8K_CMD_UPDATE_STADB 0x1123 |
286 | 288 | ||
@@ -299,6 +301,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) | |||
299 | MWL8K_CMDNAME(RADIO_CONTROL); | 301 | MWL8K_CMDNAME(RADIO_CONTROL); |
300 | MWL8K_CMDNAME(RF_TX_POWER); | 302 | MWL8K_CMDNAME(RF_TX_POWER); |
301 | MWL8K_CMDNAME(RF_ANTENNA); | 303 | MWL8K_CMDNAME(RF_ANTENNA); |
304 | MWL8K_CMDNAME(SET_BEACON); | ||
302 | MWL8K_CMDNAME(SET_PRE_SCAN); | 305 | MWL8K_CMDNAME(SET_PRE_SCAN); |
303 | MWL8K_CMDNAME(SET_POST_SCAN); | 306 | MWL8K_CMDNAME(SET_POST_SCAN); |
304 | MWL8K_CMDNAME(SET_RF_CHANNEL); | 307 | MWL8K_CMDNAME(SET_RF_CHANNEL); |
@@ -314,6 +317,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) | |||
314 | MWL8K_CMDNAME(ENABLE_SNIFFER); | 317 | MWL8K_CMDNAME(ENABLE_SNIFFER); |
315 | MWL8K_CMDNAME(SET_MAC_ADDR); | 318 | MWL8K_CMDNAME(SET_MAC_ADDR); |
316 | MWL8K_CMDNAME(SET_RATEADAPT_MODE); | 319 | MWL8K_CMDNAME(SET_RATEADAPT_MODE); |
320 | MWL8K_CMDNAME(BSS_START); | ||
317 | MWL8K_CMDNAME(SET_NEW_STN); | 321 | MWL8K_CMDNAME(SET_NEW_STN); |
318 | MWL8K_CMDNAME(UPDATE_STADB); | 322 | MWL8K_CMDNAME(UPDATE_STADB); |
319 | default: | 323 | default: |
@@ -1769,7 +1773,9 @@ struct mwl8k_cmd_set_hw_spec { | |||
1769 | __le32 total_rxd; | 1773 | __le32 total_rxd; |
1770 | } __attribute__((packed)); | 1774 | } __attribute__((packed)); |
1771 | 1775 | ||
1772 | #define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 | 1776 | #define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 |
1777 | #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 | ||
1778 | #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 | ||
1773 | 1779 | ||
1774 | static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) | 1780 | static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) |
1775 | { | 1781 | { |
@@ -1790,7 +1796,9 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) | |||
1790 | cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); | 1796 | cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); |
1791 | for (i = 0; i < MWL8K_TX_QUEUES; i++) | 1797 | for (i = 0; i < MWL8K_TX_QUEUES; i++) |
1792 | cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); | 1798 | cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); |
1793 | cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT); | 1799 | cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | |
1800 | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | | ||
1801 | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON); | ||
1794 | cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); | 1802 | cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); |
1795 | cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); | 1803 | cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); |
1796 | 1804 | ||
@@ -2028,6 +2036,35 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) | |||
2028 | } | 2036 | } |
2029 | 2037 | ||
2030 | /* | 2038 | /* |
2039 | * CMD_SET_BEACON. | ||
2040 | */ | ||
2041 | struct mwl8k_cmd_set_beacon { | ||
2042 | struct mwl8k_cmd_pkt header; | ||
2043 | __le16 beacon_len; | ||
2044 | __u8 beacon[0]; | ||
2045 | }; | ||
2046 | |||
2047 | static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, u8 *beacon, int len) | ||
2048 | { | ||
2049 | struct mwl8k_cmd_set_beacon *cmd; | ||
2050 | int rc; | ||
2051 | |||
2052 | cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); | ||
2053 | if (cmd == NULL) | ||
2054 | return -ENOMEM; | ||
2055 | |||
2056 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); | ||
2057 | cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); | ||
2058 | cmd->beacon_len = cpu_to_le16(len); | ||
2059 | memcpy(cmd->beacon, beacon, len); | ||
2060 | |||
2061 | rc = mwl8k_post_cmd(hw, &cmd->header); | ||
2062 | kfree(cmd); | ||
2063 | |||
2064 | return rc; | ||
2065 | } | ||
2066 | |||
2067 | /* | ||
2031 | * CMD_SET_PRE_SCAN. | 2068 | * CMD_SET_PRE_SCAN. |
2032 | */ | 2069 | */ |
2033 | struct mwl8k_cmd_set_pre_scan { | 2070 | struct mwl8k_cmd_set_pre_scan { |
@@ -2665,6 +2702,33 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) | |||
2665 | } | 2702 | } |
2666 | 2703 | ||
2667 | /* | 2704 | /* |
2705 | * CMD_BSS_START. | ||
2706 | */ | ||
2707 | struct mwl8k_cmd_bss_start { | ||
2708 | struct mwl8k_cmd_pkt header; | ||
2709 | __le32 enable; | ||
2710 | } __attribute__((packed)); | ||
2711 | |||
2712 | static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, int enable) | ||
2713 | { | ||
2714 | struct mwl8k_cmd_bss_start *cmd; | ||
2715 | int rc; | ||
2716 | |||
2717 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
2718 | if (cmd == NULL) | ||
2719 | return -ENOMEM; | ||
2720 | |||
2721 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); | ||
2722 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); | ||
2723 | cmd->enable = cpu_to_le32(enable); | ||
2724 | |||
2725 | rc = mwl8k_post_cmd(hw, &cmd->header); | ||
2726 | kfree(cmd); | ||
2727 | |||
2728 | return rc; | ||
2729 | } | ||
2730 | |||
2731 | /* | ||
2668 | * CMD_SET_NEW_STN. | 2732 | * CMD_SET_NEW_STN. |
2669 | */ | 2733 | */ |
2670 | struct mwl8k_cmd_set_new_stn { | 2734 | struct mwl8k_cmd_set_new_stn { |
@@ -2727,6 +2791,26 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, | |||
2727 | return rc; | 2791 | return rc; |
2728 | } | 2792 | } |
2729 | 2793 | ||
2794 | static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, | ||
2795 | struct ieee80211_vif *vif) | ||
2796 | { | ||
2797 | struct mwl8k_cmd_set_new_stn *cmd; | ||
2798 | int rc; | ||
2799 | |||
2800 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
2801 | if (cmd == NULL) | ||
2802 | return -ENOMEM; | ||
2803 | |||
2804 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); | ||
2805 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); | ||
2806 | memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); | ||
2807 | |||
2808 | rc = mwl8k_post_cmd(hw, &cmd->header); | ||
2809 | kfree(cmd); | ||
2810 | |||
2811 | return rc; | ||
2812 | } | ||
2813 | |||
2730 | static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, | 2814 | static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, |
2731 | struct ieee80211_vif *vif, u8 *addr) | 2815 | struct ieee80211_vif *vif, u8 *addr) |
2732 | { | 2816 | { |
@@ -3016,15 +3100,9 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, | |||
3016 | return -EBUSY; | 3100 | return -EBUSY; |
3017 | 3101 | ||
3018 | /* | 3102 | /* |
3019 | * We only support managed interfaces for now. | ||
3020 | */ | ||
3021 | if (vif->type != NL80211_IFTYPE_STATION) | ||
3022 | return -EINVAL; | ||
3023 | |||
3024 | /* | ||
3025 | * Reject interface creation if sniffer mode is active, as | 3103 | * Reject interface creation if sniffer mode is active, as |
3026 | * STA operation is mutually exclusive with hardware sniffer | 3104 | * STA operation is mutually exclusive with hardware sniffer |
3027 | * mode. | 3105 | * mode. (Sniffer mode is only used on STA firmware.) |
3028 | */ | 3106 | */ |
3029 | if (priv->sniffer_enabled) { | 3107 | if (priv->sniffer_enabled) { |
3030 | printk(KERN_INFO "%s: unable to create STA " | 3108 | printk(KERN_INFO "%s: unable to create STA " |
@@ -3036,6 +3114,9 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, | |||
3036 | /* Set the mac address. */ | 3114 | /* Set the mac address. */ |
3037 | mwl8k_cmd_set_mac_addr(hw, vif->addr); | 3115 | mwl8k_cmd_set_mac_addr(hw, vif->addr); |
3038 | 3116 | ||
3117 | if (priv->ap_fw) | ||
3118 | mwl8k_cmd_set_new_stn_add_self(hw, vif); | ||
3119 | |||
3039 | /* Clean out driver private area */ | 3120 | /* Clean out driver private area */ |
3040 | mwl8k_vif = MWL8K_VIF(vif); | 3121 | mwl8k_vif = MWL8K_VIF(vif); |
3041 | memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); | 3122 | memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); |
@@ -3054,6 +3135,9 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, | |||
3054 | { | 3135 | { |
3055 | struct mwl8k_priv *priv = hw->priv; | 3136 | struct mwl8k_priv *priv = hw->priv; |
3056 | 3137 | ||
3138 | if (priv->ap_fw) | ||
3139 | mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); | ||
3140 | |||
3057 | mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); | 3141 | mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); |
3058 | 3142 | ||
3059 | priv->vif = NULL; | 3143 | priv->vif = NULL; |
@@ -3105,10 +3189,9 @@ out: | |||
3105 | return rc; | 3189 | return rc; |
3106 | } | 3190 | } |
3107 | 3191 | ||
3108 | static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, | 3192 | static void |
3109 | struct ieee80211_vif *vif, | 3193 | mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
3110 | struct ieee80211_bss_conf *info, | 3194 | struct ieee80211_bss_conf *info, u32 changed) |
3111 | u32 changed) | ||
3112 | { | 3195 | { |
3113 | struct mwl8k_priv *priv = hw->priv; | 3196 | struct mwl8k_priv *priv = hw->priv; |
3114 | u32 ap_legacy_rates; | 3197 | u32 ap_legacy_rates; |
@@ -3188,6 +3271,66 @@ out: | |||
3188 | mwl8k_fw_unlock(hw); | 3271 | mwl8k_fw_unlock(hw); |
3189 | } | 3272 | } |
3190 | 3273 | ||
3274 | static void | ||
3275 | mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
3276 | struct ieee80211_bss_conf *info, u32 changed) | ||
3277 | { | ||
3278 | int rc; | ||
3279 | |||
3280 | if (mwl8k_fw_lock(hw)) | ||
3281 | return; | ||
3282 | |||
3283 | if (changed & BSS_CHANGED_ERP_PREAMBLE) { | ||
3284 | rc = mwl8k_set_radio_preamble(hw, | ||
3285 | vif->bss_conf.use_short_preamble); | ||
3286 | if (rc) | ||
3287 | goto out; | ||
3288 | } | ||
3289 | |||
3290 | if (changed & BSS_CHANGED_BASIC_RATES) { | ||
3291 | int idx; | ||
3292 | int rate; | ||
3293 | |||
3294 | /* | ||
3295 | * Use lowest supported basic rate for multicasts | ||
3296 | * and management frames (such as probe responses -- | ||
3297 | * beacons will always go out at 1 Mb/s). | ||
3298 | */ | ||
3299 | idx = ffs(vif->bss_conf.basic_rates); | ||
3300 | rate = idx ? mwl8k_rates[idx - 1].hw_value : 2; | ||
3301 | |||
3302 | mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); | ||
3303 | } | ||
3304 | |||
3305 | if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { | ||
3306 | struct sk_buff *skb; | ||
3307 | |||
3308 | skb = ieee80211_beacon_get(hw, vif); | ||
3309 | if (skb != NULL) { | ||
3310 | mwl8k_cmd_set_beacon(hw, skb->data, skb->len); | ||
3311 | kfree_skb(skb); | ||
3312 | } | ||
3313 | } | ||
3314 | |||
3315 | if (changed & BSS_CHANGED_BEACON_ENABLED) | ||
3316 | mwl8k_cmd_bss_start(hw, info->enable_beacon); | ||
3317 | |||
3318 | out: | ||
3319 | mwl8k_fw_unlock(hw); | ||
3320 | } | ||
3321 | |||
3322 | static void | ||
3323 | mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
3324 | struct ieee80211_bss_conf *info, u32 changed) | ||
3325 | { | ||
3326 | struct mwl8k_priv *priv = hw->priv; | ||
3327 | |||
3328 | if (!priv->ap_fw) | ||
3329 | mwl8k_bss_info_changed_sta(hw, vif, info, changed); | ||
3330 | else | ||
3331 | mwl8k_bss_info_changed_ap(hw, vif, info, changed); | ||
3332 | } | ||
3333 | |||
3191 | static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, | 3334 | static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, |
3192 | int mc_count, struct dev_addr_list *mclist) | 3335 | int mc_count, struct dev_addr_list *mclist) |
3193 | { | 3336 | { |
@@ -3766,6 +3909,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, | |||
3766 | rc = mwl8k_cmd_get_hw_spec_ap(hw); | 3909 | rc = mwl8k_cmd_get_hw_spec_ap(hw); |
3767 | if (!rc) | 3910 | if (!rc) |
3768 | rc = mwl8k_cmd_set_hw_spec(hw); | 3911 | rc = mwl8k_cmd_set_hw_spec(hw); |
3912 | |||
3913 | hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP); | ||
3769 | } else { | 3914 | } else { |
3770 | rc = mwl8k_cmd_get_hw_spec_sta(hw); | 3915 | rc = mwl8k_cmd_get_hw_spec_sta(hw); |
3771 | 3916 | ||