X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fmwl8k.c;h=808adb9090952347feeaafe03b801bb8ae681b62;hb=83163244f845c296a118ce85c653872dbff6abfe;hp=19c6bea1bc481558e5fa0d0707321fa483e086a6;hpb=c0adae2caa1a152c6ec691c5d1e815e47dac2a0c;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 19c6bea..808adb9 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2,7 +2,7 @@ * 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 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,7 @@ #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.10" +#define MWL8K_VERSION "0.12" /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 @@ -84,15 +85,15 @@ struct rxd_ops { int rxd_size; void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); - int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status); + int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status, + __le16 *qos); }; struct mwl8k_device_info { char *part_name; char *helper_image; char *fw_image; - struct rxd_ops *rxd_ops; - u16 modes; + struct rxd_ops *ap_rxd_ops; }; struct mwl8k_rx_queue { @@ -119,34 +120,36 @@ struct mwl8k_tx_queue { /* 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; }; -/* Pointers to the firmware data and meta information about it. */ -struct mwl8k_firmware { - /* Boot helper code */ - struct firmware *helper; +struct mwl8k_priv { + struct ieee80211_hw *hw; + struct pci_dev *pdev; - /* Microcode */ - struct firmware *ucode; -}; + struct mwl8k_device_info *device_info; -struct mwl8k_priv { void __iomem *sram; void __iomem *regs; - struct ieee80211_hw *hw; - struct pci_dev *pdev; + /* firmware */ + struct firmware *fw_helper; + struct firmware *fw_ucode; - struct mwl8k_device_info *device_info; + /* hardware/firmware parameters */ bool ap_fw; struct rxd_ops *rxd_ops; - - /* firmware files and meta data */ - struct mwl8k_firmware fw; + 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; @@ -160,9 +163,9 @@ struct mwl8k_priv { /* TX quiesce completion, protected by fw_mutex and tx_lock */ struct completion *tx_wait; - struct ieee80211_vif *vif; - - struct ieee80211_channel *current_channel; + /* List of interfaces. */ + u32 macids_used; + struct list_head vif_list; /* power management status cookie from firmware */ u32 *cookie; @@ -181,11 +184,6 @@ struct mwl8k_priv { struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES]; - /* PHY parameters */ - struct ieee80211_supported_band band; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[13]; - bool radio_on; bool radio_short_preamble; bool sniffer_enabled; @@ -204,41 +202,33 @@ struct mwl8k_priv { */ struct work_struct finalize_join_worker; - /* Tasklet to reclaim TX descriptors and buffers after tx */ - struct tasklet_struct tx_reclaim_task; + /* Tasklet to perform TX reclaim. */ + struct tasklet_struct poll_tx_task; + + /* Tasklet to perform RX. */ + struct tasklet_struct poll_rx_task; }; /* Per interface specific private data */ struct mwl8k_vif { - /* backpointer to parent config block */ - struct mwl8k_priv *priv; - - /* BSS config of AP or IBSS from mac80211*/ - struct ieee80211_bss_conf bss_info; - - /* BSSID of AP or IBSS */ - u8 bssid[ETH_ALEN]; - u8 mac_addr[ETH_ALEN]; - - /* - * Subset of supported legacy rates. - * Intersection of AP and STA supported rates. - */ - struct ieee80211_rate legacy_rates[13]; - - /* number of supported legacy rates */ - u8 legacy_nrates; + struct list_head list; + struct ieee80211_vif *vif; - /* Index into station database.Returned by update_sta_db call */ - u8 peer_id; + /* Firmware macid for this vif. */ + int macid; - /* Non AMPDU sequence number assigned by driver */ - u16 seqno; + /* Non AMPDU sequence number assigned by driver. */ + u16 seqno; }; - #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) -static const struct ieee80211_channel mwl8k_channels[] = { +struct mwl8k_sta { + /* Index into station database. Returned by UPDATE_STADB. */ + u8 peer_id; +}; +#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) + +static const struct ieee80211_channel mwl8k_channels_24[] = { { .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2417, .hw_value = 2, }, { .center_freq = 2422, .hw_value = 3, }, @@ -250,9 +240,12 @@ static const struct ieee80211_channel mwl8k_channels[] = { { .center_freq = 2452, .hw_value = 9, }, { .center_freq = 2457, .hw_value = 10, }, { .center_freq = 2462, .hw_value = 11, }, + { .center_freq = 2467, .hw_value = 12, }, + { .center_freq = 2472, .hw_value = 13, }, + { .center_freq = 2484, .hw_value = 14, }, }; -static const struct ieee80211_rate mwl8k_rates[] = { +static const struct ieee80211_rate mwl8k_rates_24[] = { { .bitrate = 10, .hw_value = 2, }, { .bitrate = 20, .hw_value = 4, }, { .bitrate = 55, .hw_value = 11, }, @@ -266,6 +259,26 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 360, .hw_value = 72, }, { .bitrate = 480, .hw_value = 96, }, { .bitrate = 540, .hw_value = 108, }, + { .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 */ @@ -281,6 +294,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 /* per-vif */ #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a @@ -294,8 +308,10 @@ static const struct ieee80211_rate mwl8k_rates[] = { #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 /* 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) @@ -313,6 +329,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); @@ -328,6 +345,8 @@ 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: snprintf(buf, bufsize, "0x%x", cmd); @@ -358,8 +377,8 @@ static void mwl8k_release_fw(struct firmware **fw) static void mwl8k_release_firmware(struct mwl8k_priv *priv) { - mwl8k_release_fw(&priv->fw.ucode); - mwl8k_release_fw(&priv->fw.helper); + mwl8k_release_fw(&priv->fw_ucode); + mwl8k_release_fw(&priv->fw_helper); } /* Request fw image */ @@ -380,7 +399,7 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) int rc; if (di->helper_image != NULL) { - rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw.helper); + rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper); if (rc) { printk(KERN_ERR "%s: Error requesting helper " "firmware file %s\n", pci_name(priv->pdev), @@ -389,11 +408,11 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) } } - rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw.ucode); + rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw_ucode); if (rc) { printk(KERN_ERR "%s: Error requesting firmware file %s\n", pci_name(priv->pdev), di->fw_image); - mwl8k_release_fw(&priv->fw.helper); + mwl8k_release_fw(&priv->fw_helper); return rc; } @@ -403,7 +422,8 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) struct mwl8k_cmd_pkt { __le16 code; __le16 length; - __le16 seq_num; + __u8 seq_num; + __u8 macid; __le16 result; char payload[0]; } __attribute__((packed)); @@ -461,6 +481,7 @@ static int mwl8k_load_fw_image(struct mwl8k_priv *priv, cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); cmd->seq_num = 0; + cmd->macid = 0; cmd->result = 0; done = 0; @@ -551,13 +572,12 @@ static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, static int mwl8k_load_firmware(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; - struct firmware *fw = priv->fw.ucode; - struct mwl8k_device_info *di = priv->device_info; + struct firmware *fw = priv->fw_ucode; int rc; int loops; if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) { - struct firmware *helper = priv->fw.helper; + struct firmware *helper = priv->fw_helper; if (helper == NULL) { printk(KERN_ERR "%s: helper image needed but none " @@ -571,7 +591,7 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) "helper image\n", pci_name(priv->pdev)); return rc; } - msleep(1); + msleep(5); rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); } else { @@ -584,13 +604,9 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) return rc; } - if (di->modes & BIT(NL80211_IFTYPE_AP)) - iowrite32(MWL8K_MODE_AP, priv->regs + MWL8K_HIU_GEN_PTR); - else - iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); - msleep(1); + iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); - loops = 200000; + loops = 500000; do { u32 ready_code; @@ -611,154 +627,171 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) } -/* - * Defines shared between transmission and reception. - */ -/* HT control fields for firmware */ -struct ewc_ht_info { - __le16 control1; - __le16 control2; - __le16 control3; +/* DMA header used by firmware and hardware. */ +struct mwl8k_dma_data { + __le16 fwlen; + struct ieee80211_hdr wh; + char data[0]; } __attribute__((packed)); -/* Firmware Station database operations */ -#define MWL8K_STA_DB_ADD_ENTRY 0 -#define MWL8K_STA_DB_MODIFY_ENTRY 1 -#define MWL8K_STA_DB_DEL_ENTRY 2 -#define MWL8K_STA_DB_FLUSH 3 +/* Routines to add/remove DMA header from skb. */ +static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos) +{ + struct mwl8k_dma_data *tr; + int hdrlen; -/* Peer Entry flags - used to define the type of the peer node */ -#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + tr = (struct mwl8k_dma_data *)skb->data; + hdrlen = ieee80211_hdrlen(tr->wh.frame_control); -#define MWL8K_IEEE_LEGACY_DATA_RATES 13 -#define MWL8K_MCS_BITMAP_SIZE 16 + if (hdrlen != sizeof(tr->wh)) { + if (ieee80211_is_data_qos(tr->wh.frame_control)) { + memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2); + *((__le16 *)(tr->data - 2)) = qos; + } else { + memmove(tr->data - hdrlen, &tr->wh, hdrlen); + } + } -struct peer_capability_info { - /* Peer type - AP vs. STA. */ - __u8 peer_type; + if (hdrlen != sizeof(*tr)) + skb_pull(skb, sizeof(*tr) - hdrlen); +} - /* Basic 802.11 capabilities from assoc resp. */ - __le16 basic_caps; +static inline void mwl8k_add_dma_header(struct sk_buff *skb) +{ + struct ieee80211_hdr *wh; + int hdrlen; + struct mwl8k_dma_data *tr; - /* Set if peer supports 802.11n high throughput (HT). */ - __u8 ht_support; + /* + * Add a firmware DMA header; the firmware requires that we + * present a 2-byte payload length followed by a 4-address + * header (without QoS field), followed (optionally) by any + * WEP/ExtIV header (but only filled in for CCMP). + */ + wh = (struct ieee80211_hdr *)skb->data; - /* Valid if HT is supported. */ - __le16 ht_caps; - __u8 extended_ht_caps; - struct ewc_ht_info ewc_info; + hdrlen = ieee80211_hdrlen(wh->frame_control); + if (hdrlen != sizeof(*tr)) + skb_push(skb, sizeof(*tr) - hdrlen); - /* Legacy rate table. Intersection of our rates and peer rates. */ - __u8 legacy_rates[MWL8K_IEEE_LEGACY_DATA_RATES]; + if (ieee80211_is_data_qos(wh->frame_control)) + hdrlen -= 2; - /* HT rate table. Intersection of our rates and peer rates. */ - __u8 ht_rates[MWL8K_MCS_BITMAP_SIZE]; - __u8 pad[16]; + tr = (struct mwl8k_dma_data *)skb->data; + if (wh != &tr->wh) + memmove(&tr->wh, wh, hdrlen); + if (hdrlen != sizeof(tr->wh)) + memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen); - /* If set, interoperability mode, no proprietary extensions. */ - __u8 interop; - __u8 pad2; - __u8 station_id; - __le16 amsdu_enabled; -} __attribute__((packed)); + /* + * Firmware length is the length of the fully formed "802.11 + * payload". That is, everything except for the 802.11 header. + * This includes all crypto material including the MIC. + */ + tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr)); +} -/* Inline functions to manipulate QoS field in data descriptor. */ -static inline u16 mwl8k_qos_setbit_eosp(u16 qos) -{ - u16 val_mask = 1 << 4; - /* End of Service Period Bit 4 */ - return qos | val_mask; -} +/* + * Packet reception for 88w8366 AP firmware. + */ +struct mwl8k_rxd_8366_ap { + __le16 pkt_len; + __u8 sq2; + __u8 rate; + __le32 pkt_phys_addr; + __le32 next_rxd_phys_addr; + __le16 qos_control; + __le16 htsig2; + __le32 hw_rssi_info; + __le32 hw_noise_floor_info; + __u8 noise_floor; + __u8 pad0[3]; + __u8 rssi; + __u8 rx_status; + __u8 channel; + __u8 rx_ctrl; +} __attribute__((packed)); -static inline u16 mwl8k_qos_setbit_ack(u16 qos, u8 ack_policy) -{ - u16 val_mask = 0x3; - u8 shift = 5; - u16 qos_mask = ~(val_mask << shift); +#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT 0x80 +#define MWL8K_8366_AP_RATE_INFO_40MHZ 0x40 +#define MWL8K_8366_AP_RATE_INFO_RATEID(x) ((x) & 0x3f) - /* Ack Policy Bit 5-6 */ - return (qos & qos_mask) | ((ack_policy & val_mask) << shift); -} +#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80 -static inline u16 mwl8k_qos_setbit_amsdu(u16 qos) +static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr) { - u16 val_mask = 1 << 7; + struct mwl8k_rxd_8366_ap *rxd = _rxd; - /* AMSDU present Bit 7 */ - return qos | val_mask; + rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); + rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST; } -static inline u16 mwl8k_qos_setbit_qlen(u16 qos, u8 len) +static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len) { - u16 val_mask = 0xff; - u8 shift = 8; - u16 qos_mask = ~(val_mask << shift); + struct mwl8k_rxd_8366_ap *rxd = _rxd; - /* Queue Length Bits 8-15 */ - return (qos & qos_mask) | ((len & val_mask) << shift); + rxd->pkt_len = cpu_to_le16(len); + rxd->pkt_phys_addr = cpu_to_le32(addr); + wmb(); + rxd->rx_ctrl = 0; } -/* DMA header used by firmware and hardware. */ -struct mwl8k_dma_data { - __le16 fwlen; - struct ieee80211_hdr wh; -} __attribute__((packed)); - -/* Routines to add/remove DMA header from skb. */ -static inline void mwl8k_remove_dma_header(struct sk_buff *skb) +static int +mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos) { - struct mwl8k_dma_data *tr = (struct mwl8k_dma_data *)skb->data; - void *dst, *src = &tr->wh; - int hdrlen = ieee80211_hdrlen(tr->wh.frame_control); - u16 space = sizeof(struct mwl8k_dma_data) - hdrlen; + struct mwl8k_rxd_8366_ap *rxd = _rxd; - dst = (void *)tr + space; - if (dst != src) { - memmove(dst, src, hdrlen); - skb_pull(skb, space); - } -} + if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST)) + return -1; + rmb(); -static inline void mwl8k_add_dma_header(struct sk_buff *skb) -{ - struct ieee80211_hdr *wh; - u32 hdrlen, pktlen; - struct mwl8k_dma_data *tr; + memset(status, 0, sizeof(*status)); - wh = (struct ieee80211_hdr *)skb->data; - hdrlen = ieee80211_hdrlen(wh->frame_control); - pktlen = skb->len; + status->signal = -rxd->rssi; - /* - * Copy up/down the 802.11 header; the firmware requires - * we present a 2-byte payload length followed by a - * 4-address header (w/o QoS), followed (optionally) by - * any WEP/ExtIV header (but only filled in for CCMP). - */ - if (hdrlen != sizeof(struct mwl8k_dma_data)) - skb_push(skb, sizeof(struct mwl8k_dma_data) - hdrlen); + if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) { + status->flag |= RX_FLAG_HT; + if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ) + status->flag |= RX_FLAG_40MHZ; + status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate); + } else { + int i; - tr = (struct mwl8k_dma_data *)skb->data; - if (wh != &tr->wh) - memmove(&tr->wh, wh, hdrlen); + for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { + if (mwl8k_rates_24[i].hw_value == rxd->rate) { + status->rate_idx = i; + break; + } + } + } - /* Clear addr4 */ - memset(tr->wh.addr4, 0, ETH_ALEN); + 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); - /* - * Firmware length is the length of the fully formed "802.11 - * payload". That is, everything except for the 802.11 header. - * This includes all crypto material including the MIC. - */ - tr->fwlen = cpu_to_le16(pktlen - hdrlen); + *qos = rxd->qos_control; + + return le16_to_cpu(rxd->pkt_len); } +static struct rxd_ops rxd_8366_ap_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_8366_ap), + .rxd_init = mwl8k_rxd_8366_ap_init, + .rxd_refill = mwl8k_rxd_8366_ap_refill, + .rxd_process = mwl8k_rxd_8366_ap_process, +}; /* - * Packet reception. + * Packet reception for STA firmware. */ -struct mwl8k_rxd_8687 { +struct mwl8k_rxd_sta { __le16 pkt_len; __u8 link_quality; __u8 noise_level; @@ -775,26 +808,26 @@ struct mwl8k_rxd_8687 { __u8 pad2[2]; } __attribute__((packed)); -#define MWL8K_8687_RATE_INFO_SHORTPRE 0x8000 -#define MWL8K_8687_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) -#define MWL8K_8687_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) -#define MWL8K_8687_RATE_INFO_40MHZ 0x0004 -#define MWL8K_8687_RATE_INFO_SHORTGI 0x0002 -#define MWL8K_8687_RATE_INFO_MCS_FORMAT 0x0001 +#define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000 +#define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) +#define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) +#define MWL8K_STA_RATE_INFO_40MHZ 0x0004 +#define MWL8K_STA_RATE_INFO_SHORTGI 0x0002 +#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001 -#define MWL8K_8687_RX_CTRL_OWNED_BY_HOST 0x02 +#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02 -static void mwl8k_rxd_8687_init(void *_rxd, dma_addr_t next_dma_addr) +static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr) { - struct mwl8k_rxd_8687 *rxd = _rxd; + struct mwl8k_rxd_sta *rxd = _rxd; rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); - rxd->rx_ctrl = MWL8K_8687_RX_CTRL_OWNED_BY_HOST; + rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST; } -static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len) +static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len) { - struct mwl8k_rxd_8687 *rxd = _rxd; + struct mwl8k_rxd_sta *rxd = _rxd; rxd->pkt_len = cpu_to_le16(len); rxd->pkt_phys_addr = cpu_to_le32(addr); @@ -803,12 +836,13 @@ static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len) } static int -mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status) +mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos) { - struct mwl8k_rxd_8687 *rxd = _rxd; + struct mwl8k_rxd_sta *rxd = _rxd; u16 rate_info; - if (!(rxd->rx_ctrl & MWL8K_8687_RX_CTRL_OWNED_BY_HOST)) + if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST)) return -1; rmb(); @@ -817,31 +851,37 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status) memset(status, 0, sizeof(*status)); status->signal = -rxd->rssi; - status->noise = -rxd->noise_level; - status->qual = rxd->link_quality; - status->antenna = MWL8K_8687_RATE_INFO_ANTSELECT(rate_info); - status->rate_idx = MWL8K_8687_RATE_INFO_RATEID(rate_info); + status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info); + status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); - if (rate_info & MWL8K_8687_RATE_INFO_SHORTPRE) + if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) status->flag |= RX_FLAG_SHORTPRE; - if (rate_info & MWL8K_8687_RATE_INFO_40MHZ) + if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) status->flag |= RX_FLAG_40MHZ; - if (rate_info & MWL8K_8687_RATE_INFO_SHORTGI) + if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) status->flag |= RX_FLAG_SHORT_GI; - if (rate_info & MWL8K_8687_RATE_INFO_MCS_FORMAT) + 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; + return le16_to_cpu(rxd->pkt_len); } -static struct rxd_ops rxd_8687_ops = { - .rxd_size = sizeof(struct mwl8k_rxd_8687), - .rxd_init = mwl8k_rxd_8687_init, - .rxd_refill = mwl8k_rxd_8687_refill, - .rxd_process = mwl8k_rxd_8687_process, +static struct rxd_ops rxd_sta_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_sta), + .rxd_init = mwl8k_rxd_sta_init, + .rxd_refill = mwl8k_rxd_sta_refill, + .rxd_process = mwl8k_rxd_sta_process, }; @@ -1005,6 +1045,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) void *rxd; int pkt_len; struct ieee80211_rx_status status; + __le16 qos; skb = rxq->buf[rxq->head].skb; if (skb == NULL) @@ -1012,7 +1053,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); - pkt_len = priv->rxd_ops->rxd_process(rxd, &status); + pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos); if (pkt_len < 0) break; @@ -1030,7 +1071,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) rxq->rxd_count--; skb_put(skb, pkt_len); - mwl8k_remove_dma_header(skb); + mwl8k_remove_dma_header(skb, qos); /* * Check for a pending join operation. Save a @@ -1054,16 +1095,18 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) * Packet transmission. */ -/* Transmit packet ACK policy */ -#define MWL8K_TXD_ACK_POLICY_NORMAL 0 -#define MWL8K_TXD_ACK_POLICY_BLOCKACK 3 - #define MWL8K_TXD_STATUS_OK 0x00000001 #define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 #define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 #define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 #define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 +#define MWL8K_QOS_QLEN_UNSPEC 0xff00 +#define MWL8K_QOS_ACK_POLICY_MASK 0x0060 +#define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000 +#define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060 +#define MWL8K_QOS_EOSP 0x0010 + struct mwl8k_tx_desc { __le32 status; __u8 data_rate; @@ -1088,8 +1131,7 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) 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; @@ -1136,99 +1178,106 @@ static inline void mwl8k_tx_start(struct mwl8k_priv *priv) ioread32(priv->regs + MWL8K_HIU_INT_CODE); } -struct mwl8k_txq_info { - u32 fw_owned; - u32 drv_owned; - u32 unused; - u32 len; - u32 head; - u32 tail; -}; - -static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, - struct mwl8k_txq_info *txinfo) +static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) { - int count, desc, status; - struct mwl8k_tx_queue *txq; - struct mwl8k_tx_desc *tx_desc; - int ndescs = 0; + struct mwl8k_priv *priv = hw->priv; + int i; - memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info)); + for (i = 0; i < MWL8K_TX_QUEUES; i++) { + struct mwl8k_tx_queue *txq = priv->txq + i; + int fw_owned = 0; + int drv_owned = 0; + int unused = 0; + int desc; - for (count = 0; count < MWL8K_TX_QUEUES; count++) { - txq = priv->txq + count; - txinfo[count].len = txq->stats.len; - txinfo[count].head = txq->head; - txinfo[count].tail = txq->tail; for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { - tx_desc = txq->txd + desc; - status = le32_to_cpu(tx_desc->status); + struct mwl8k_tx_desc *tx_desc = txq->txd + desc; + u32 status; + status = le32_to_cpu(tx_desc->status); if (status & MWL8K_TXD_STATUS_FW_OWNED) - txinfo[count].fw_owned++; + fw_owned++; else - txinfo[count].drv_owned++; + drv_owned++; if (tx_desc->pkt_len == 0) - txinfo[count].unused++; + unused++; } - } - return ndescs; + 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->len, txq->head, txq->tail, + fw_owned, drv_owned, unused); + } } /* * Must be called with priv->fw_mutex held and tx queues stopped. */ +#define MWL8K_TX_WAIT_TIMEOUT_MS 5000 + static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; DECLARE_COMPLETION_ONSTACK(tx_wait); - u32 count; - unsigned long timeout; + int retry; + int rc; might_sleep(); + /* + * The TX queues are stopped at this point, so this test + * doesn't need to take ->tx_lock. + */ + if (!priv->pending_tx_pkts) + return 0; + + retry = 0; + rc = 0; + spin_lock_bh(&priv->tx_lock); - count = priv->pending_tx_pkts; - if (count) - priv->tx_wait = &tx_wait; - spin_unlock_bh(&priv->tx_lock); + priv->tx_wait = &tx_wait; + while (!rc) { + int oldcount; + unsigned long timeout; - if (count) { - struct mwl8k_txq_info txinfo[MWL8K_TX_QUEUES]; - int index; - int newcount; + oldcount = priv->pending_tx_pkts; + spin_unlock_bh(&priv->tx_lock); timeout = wait_for_completion_timeout(&tx_wait, - msecs_to_jiffies(5000)); - if (timeout) - return 0; - + msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); spin_lock_bh(&priv->tx_lock); - priv->tx_wait = NULL; - newcount = priv->pending_tx_pkts; - mwl8k_scan_tx_ring(priv, txinfo); - spin_unlock_bh(&priv->tx_lock); - printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n", - __func__, __LINE__, count, newcount); + if (timeout) { + WARN_ON(priv->pending_tx_pkts); + if (retry) { + printk(KERN_NOTICE "%s: tx rings drained\n", + wiphy_name(hw->wiphy)); + } + break; + } + + if (priv->pending_tx_pkts < oldcount) { + printk(KERN_NOTICE "%s: waiting for tx rings " + "to drain (%d -> %d pkts)\n", + wiphy_name(hw->wiphy), oldcount, + priv->pending_tx_pkts); + retry = 1; + continue; + } + + priv->tx_wait = NULL; - for (index = 0; index < MWL8K_TX_QUEUES; index++) - printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u " - "DRV:%u U:%u\n", - index, - txinfo[index].len, - txinfo[index].head, - txinfo[index].tail, - txinfo[index].fw_owned, - txinfo[index].drv_owned, - txinfo[index].unused); + printk(KERN_ERR "%s: tx rings stuck for %d ms\n", + wiphy_name(hw->wiphy), MWL8K_TX_WAIT_TIMEOUT_MS); + mwl8k_dump_tx_rings(hw); - return -ETIMEDOUT; + rc = -ETIMEDOUT; } + spin_unlock_bh(&priv->tx_lock); - return 0; + return rc; } #define MWL8K_TXD_SUCCESS(status) \ @@ -1236,13 +1285,15 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) MWL8K_TXD_STATUS_OK_RETRY | \ MWL8K_TXD_STATUS_OK_MORE_RETRY)) -static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) +static int +mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - int wake = 0; + int processed; - while (txq->stats.len > 0) { + processed = 0; + while (txq->len > 0 && limit--) { int tx; struct mwl8k_tx_desc *tx_desc; unsigned long addr; @@ -1264,8 +1315,8 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) } 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); @@ -1276,7 +1327,7 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) BUG_ON(skb == NULL); pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); - mwl8k_remove_dma_header(skb); + mwl8k_remove_dma_header(skb, tx_desc->qos_control); /* Mark descriptor as unused */ tx_desc->pkt_phys_addr = 0; @@ -1289,11 +1340,13 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) ieee80211_tx_status_irqsafe(hw, skb); - wake = 1; + processed++; } - if (wake && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) + if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) ieee80211_wake_queue(hw, index); + + return processed; } /* must be called only when the card's transmit is completely halted */ @@ -1302,7 +1355,7 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - mwl8k_txq_reclaim(hw, index, 1); + mwl8k_txq_reclaim(hw, index, INT_MAX, 1); kfree(txq->skb); txq->skb = NULL; @@ -1340,11 +1393,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) mwl8k_vif = MWL8K_VIF(tx_info->control.vif); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - u16 seqno = mwl8k_vif->seqno; - wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - wh->seq_ctrl |= cpu_to_le16(seqno << 4); - mwl8k_vif->seqno = seqno++ % 4096; + wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); + mwl8k_vif->seqno += 0x10; } /* Setup firmware control bit fields for each frame type. */ @@ -1353,24 +1404,17 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) if (ieee80211_is_mgmt(wh->frame_control) || ieee80211_is_ctl(wh->frame_control)) { txdatarate = 0; - qos = mwl8k_qos_setbit_eosp(qos); - /* Set Queue size to unspecified */ - qos = mwl8k_qos_setbit_qlen(qos, 0xff); + qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP; } else if (ieee80211_is_data(wh->frame_control)) { txdatarate = 1; if (is_multicast_ether_addr(wh->addr1)) txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; - /* Send pkt in an aggregate if AMPDU frame. */ + qos &= ~MWL8K_QOS_ACK_POLICY_MASK; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_BLOCKACK); + qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK; else - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_NORMAL); - - if (qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) - qos = mwl8k_qos_setbit_amsdu(qos); + qos |= MWL8K_QOS_ACK_POLICY_NORMAL; } dma = pci_map_single(priv->pdev, skb->data, @@ -1397,12 +1441,14 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); tx->rate_info = 0; - tx->peer_id = mwl8k_vif->peer_id; + if (!priv->ap_fw && tx_info->control.sta != NULL) + tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id; + else + tx->peer_id = 0; 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++; @@ -1478,8 +1524,8 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw) * Command processing. */ -/* Timeout firmware commands after 2000ms */ -#define MWL8K_CMD_TIMEOUT_MS 2000 +/* Timeout firmware commands after 10s */ +#define MWL8K_CMD_TIMEOUT_MS 10000 static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) { @@ -1530,17 +1576,76 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) MWL8K_CMD_TIMEOUT_MS); rc = -ETIMEDOUT; } else { + int ms; + + ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout); + rc = cmd->result ? -EINVAL : 0; if (rc) printk(KERN_ERR "%s: Command %s error 0x%x\n", wiphy_name(hw->wiphy), mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), le16_to_cpu(cmd->result)); + else if (ms > 2000) + printk(KERN_NOTICE "%s: Command %s took %d ms\n", + wiphy_name(hw->wiphy), + mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), + ms); } 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). */ @@ -1563,6 +1668,89 @@ struct mwl8k_cmd_get_hw_spec_sta { __le32 total_rxd; } __attribute__((packed)); +#define MWL8K_CAP_MAX_AMSDU 0x20000000 +#define MWL8K_CAP_GREENFIELD 0x08000000 +#define MWL8K_CAP_AMPDU 0x04000000 +#define MWL8K_CAP_RX_STBC 0x01000000 +#define MWL8K_CAP_TX_STBC 0x00800000 +#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 +#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 +#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 +#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 +#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, + struct ieee80211_supported_band *band, u32 cap) +{ + int rx_streams; + int tx_streams; + + band->ht_cap.ht_supported = 1; + + if (cap & MWL8K_CAP_MAX_AMSDU) + band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + if (cap & MWL8K_CAP_GREENFIELD) + band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; + if (cap & MWL8K_CAP_AMPDU) { + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + } + if (cap & MWL8K_CAP_RX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; + if (cap & MWL8K_CAP_TX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + if (cap & MWL8K_CAP_SHORTGI_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + if (cap & MWL8K_CAP_SHORTGI_20MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + if (cap & MWL8K_CAP_DELAY_BA) + band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; + if (cap & MWL8K_CAP_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); + tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); + + band->ht_cap.mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + band->ht_cap.mcs.rx_mask[1] = 0xff; + if (rx_streams >= 3) + band->ht_cap.mcs.rx_mask[2] = 0xff; + band->ht_cap.mcs.rx_mask[4] = 0x01; + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + if (rx_streams != tx_streams) { + band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + band->ht_cap.mcs.tx_params |= (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + } +} + +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; @@ -1593,6 +1781,9 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) 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_set_caps(hw, le32_to_cpu(cmd->caps)); + priv->ap_macids_supported = 0x00000000; + priv->sta_macids_supported = 0x00000001; } kfree(cmd); @@ -1646,6 +1837,9 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) 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); @@ -1691,7 +1885,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) { @@ -1712,7 +1908,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); @@ -1739,11 +1937,15 @@ struct mwl8k_cmd_mac_multicast_adr { 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; @@ -1764,17 +1966,13 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, 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); } } @@ -1782,9 +1980,9 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, } /* - * CMD_802_11_GET_STAT. + * CMD_GET_STAT. */ -struct mwl8k_cmd_802_11_get_stat { +struct mwl8k_cmd_get_stat { struct mwl8k_cmd_pkt header; __le32 stats[64]; } __attribute__((packed)); @@ -1794,10 +1992,10 @@ struct mwl8k_cmd_802_11_get_stat { #define MWL8K_STAT_FCS_ERROR 24 #define MWL8K_STAT_RTS_SUCCESS 11 -static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) +static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) { - struct mwl8k_cmd_802_11_get_stat *cmd; + struct mwl8k_cmd_get_stat *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1824,9 +2022,9 @@ static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw, } /* - * CMD_802_11_RADIO_CONTROL. + * CMD_RADIO_CONTROL. */ -struct mwl8k_cmd_802_11_radio_control { +struct mwl8k_cmd_radio_control { struct mwl8k_cmd_pkt header; __le16 action; __le16 control; @@ -1834,10 +2032,10 @@ struct mwl8k_cmd_802_11_radio_control { } __attribute__((packed)); static int -mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force) +mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_802_11_radio_control *cmd; + struct mwl8k_cmd_radio_control *cmd; int rc; if (enable == priv->radio_on && !force) @@ -1862,36 +2060,32 @@ mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force) return rc; } -static int mwl8k_cmd_802_11_radio_disable(struct ieee80211_hw *hw) +static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw) { - return mwl8k_cmd_802_11_radio_control(hw, 0, 0); + return mwl8k_cmd_radio_control(hw, 0, 0); } -static int mwl8k_cmd_802_11_radio_enable(struct ieee80211_hw *hw) +static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw) { - return mwl8k_cmd_802_11_radio_control(hw, 1, 0); + return mwl8k_cmd_radio_control(hw, 1, 0); } static int mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) { - struct mwl8k_priv *priv; - - if (hw == NULL || hw->priv == NULL) - return -EINVAL; - priv = hw->priv; + struct mwl8k_priv *priv = hw->priv; priv->radio_short_preamble = short_preamble; - return mwl8k_cmd_802_11_radio_control(hw, 1, 1); + return mwl8k_cmd_radio_control(hw, 1, 1); } /* - * CMD_802_11_RF_TX_POWER. + * CMD_RF_TX_POWER. */ #define MWL8K_TX_POWER_LEVEL_TOTAL 8 -struct mwl8k_cmd_802_11_rf_tx_power { +struct mwl8k_cmd_rf_tx_power { struct mwl8k_cmd_pkt header; __le16 action; __le16 support_level; @@ -1900,9 +2094,9 @@ struct mwl8k_cmd_802_11_rf_tx_power { __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; } __attribute__((packed)); -static int mwl8k_cmd_802_11_rf_tx_power(struct ieee80211_hw *hw, int dBm) +static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm) { - struct mwl8k_cmd_802_11_rf_tx_power *cmd; + struct mwl8k_cmd_rf_tx_power *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1954,6 +2148,36 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) } /* + * 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, + struct ieee80211_vif *vif, 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_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* * CMD_SET_PRE_SCAN. */ struct mwl8k_cmd_set_pre_scan { @@ -1988,7 +2212,7 @@ struct mwl8k_cmd_set_post_scan { } __attribute__((packed)); static int -mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 *mac) +mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) { struct mwl8k_cmd_set_post_scan *cmd; int rc; @@ -2019,8 +2243,9 @@ struct mwl8k_cmd_set_rf_channel { } __attribute__((packed)); static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, - struct ieee80211_channel *channel) + struct ieee80211_conf *conf) { + struct ieee80211_channel *channel = conf->channel; struct mwl8k_cmd_set_rf_channel *cmd; int rc; @@ -2032,10 +2257,19 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->current_channel = channel->hw_value; + if (channel->band == IEEE80211_BAND_2GHZ) - cmd->channel_flags = cpu_to_le32(0x00000081); - else - cmd->channel_flags = cpu_to_le32(0x00000000); + 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) + cmd->channel_flags |= cpu_to_le32(0x00000080); + else if (conf->channel_type == NL80211_CHAN_HT40MINUS) + cmd->channel_flags |= cpu_to_le32(0x000001900); + else if (conf->channel_type == NL80211_CHAN_HT40PLUS) + cmd->channel_flags |= cpu_to_le32(0x000000900); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2044,27 +2278,75 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, } /* - * CMD_SET_SLOT. + * CMD_SET_AID. */ -struct mwl8k_cmd_set_slot { - struct mwl8k_cmd_pkt header; - __le16 action; - __u8 short_slot; +#define MWL8K_FRAME_PROT_DISABLED 0x00 +#define MWL8K_FRAME_PROT_11G 0x07 +#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 +#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 + +struct mwl8k_cmd_update_set_aid { + struct mwl8k_cmd_pkt header; + __le16 aid; + + /* AP's MAC address (BSSID) */ + __u8 bssid[ETH_ALEN]; + __le16 protection_mode; + __u8 supp_rates[14]; } __attribute__((packed)); -static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) +static void legacy_rate_mask_to_array(u8 *rates, u32 mask) { - struct mwl8k_cmd_set_slot *cmd; + int i; + int j; + + /* + * Clear nonstandard rates 4 and 13. + */ + mask &= 0x1fef; + + for (i = 0, j = 0; i < 14; i++) { + if (mask & (1 << i)) + rates[j++] = mwl8k_rates_24[i].hw_value; + } +} + +static int +mwl8k_cmd_set_aid(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 legacy_rate_mask) +{ + struct mwl8k_cmd_update_set_aid *cmd; + u16 prot_mode; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->short_slot = short_slot_time; + cmd->aid = cpu_to_le16(vif->bss_conf.aid); + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); + + if (vif->bss_conf.use_cts_prot) { + prot_mode = MWL8K_FRAME_PROT_11G; + } else { + switch (vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; + break; + default: + prot_mode = MWL8K_FRAME_PROT_DISABLED; + break; + } + } + cmd->protection_mode = cpu_to_le16(prot_mode); + + legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2073,29 +2355,32 @@ static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) } /* - * CMD_MIMO_CONFIG. + * CMD_SET_RATE. */ -struct mwl8k_cmd_mimo_config { - struct mwl8k_cmd_pkt header; - __le32 action; - __u8 rx_antenna_map; - __u8 tx_antenna_map; +struct mwl8k_cmd_set_rate { + struct mwl8k_cmd_pkt header; + __u8 legacy_rates[14]; + + /* Bitmap for supported MCS codes. */ + __u8 mcs_set[16]; + __u8 reserved[16]; } __attribute__((packed)); -static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) +static int +mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 legacy_rate_mask, u8 *mcs_rates) { - struct mwl8k_cmd_mimo_config *cmd; + struct mwl8k_cmd_set_rate *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); - cmd->rx_antenna_map = rx; - cmd->tx_antenna_map = tx; + legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); + memcpy(cmd->mcs_set, mcs_rates, 16); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2104,25 +2389,39 @@ static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) } /* - * CMD_ENABLE_SNIFFER. + * CMD_FINALIZE_JOIN. */ -struct mwl8k_cmd_enable_sniffer { +#define MWL8K_FJ_BEACON_MAXLEN 128 + +struct mwl8k_cmd_finalize_join { struct mwl8k_cmd_pkt header; - __le32 action; + __le32 sleep_interval; /* Number of beacon periods to sleep */ + __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; } __attribute__((packed)); -static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) +static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame, + int framelen, int dtim) { - struct mwl8k_cmd_enable_sniffer *cmd; + struct mwl8k_cmd_finalize_join *cmd; + struct ieee80211_mgmt *payload = frame; + int payload_len; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(!!enable); + cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); + + payload_len = framelen - ieee80211_hdrlen(payload->frame_control); + if (payload_len < 0) + payload_len = 0; + else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) + payload_len = MWL8K_FJ_BEACON_MAXLEN; + + memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2131,25 +2430,28 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) } /* - * CMD_SET_MAC_ADDR. + * CMD_SET_RTS_THRESHOLD. */ -struct mwl8k_cmd_set_mac_addr { +struct mwl8k_cmd_set_rts_threshold { struct mwl8k_cmd_pkt header; - __u8 mac_addr[ETH_ALEN]; + __le16 action; + __le16 threshold; } __attribute__((packed)); -static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) +static int +mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) { - struct mwl8k_cmd_set_mac_addr *cmd; + struct mwl8k_cmd_set_rts_threshold *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); + cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - memcpy(cmd->mac_addr, mac, ETH_ALEN); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->threshold = cpu_to_le16(rts_thresh); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2157,29 +2459,113 @@ static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) return rc; } - /* - * CMD_SET_RATEADAPT_MODE. + * CMD_SET_SLOT. */ -struct mwl8k_cmd_set_rate_adapt_mode { +struct mwl8k_cmd_set_slot { struct mwl8k_cmd_pkt header; __le16 action; - __le16 mode; + __u8 short_slot; } __attribute__((packed)); -static int mwl8k_cmd_setrateadaptmode(struct ieee80211_hw *hw, __u16 mode) +static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) { - struct mwl8k_cmd_set_rate_adapt_mode *cmd; + struct mwl8k_cmd_set_slot *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->mode = cpu_to_le16(mode); + cmd->short_slot = short_slot_time; + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_EDCA_PARAMS. + */ +struct mwl8k_cmd_set_edca_params { + struct mwl8k_cmd_pkt header; + + /* See MWL8K_SET_EDCA_XXX below */ + __le16 action; + + /* TX opportunity in units of 32 us */ + __le16 txop; + + union { + struct { + /* Log exponent of max contention period: 0...15 */ + __le32 log_cw_max; + + /* Log exponent of min contention period: 0...15 */ + __le32 log_cw_min; + + /* Adaptive interframe spacing in units of 32us */ + __u8 aifs; + + /* TX queue to configure */ + __u8 txq; + } ap; + struct { + /* Log exponent of max contention period: 0...15 */ + __u8 log_cw_max; + + /* Log exponent of min contention period: 0...15 */ + __u8 log_cw_min; + + /* Adaptive interframe spacing in units of 32us */ + __u8 aifs; + + /* TX queue to configure */ + __u8 txq; + } sta; + }; +} __attribute__((packed)); + +#define MWL8K_SET_EDCA_CW 0x01 +#define MWL8K_SET_EDCA_TXOP 0x02 +#define MWL8K_SET_EDCA_AIFS 0x04 + +#define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \ + MWL8K_SET_EDCA_TXOP | \ + MWL8K_SET_EDCA_AIFS) + +static int +mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, + __u16 cw_min, __u16 cw_max, + __u8 aifs, __u16 txop) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_edca_params *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); + cmd->txop = cpu_to_le16(txop); + if (priv->ap_fw) { + cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1)); + cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1)); + cmd->ap.aifs = aifs; + cmd->ap.txq = qnum; + } else { + cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1); + cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1); + cmd->sta.aifs = aifs; + cmd->sta.txq = qnum; + } rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2190,15 +2576,15 @@ static int mwl8k_cmd_setrateadaptmode(struct ieee80211_hw *hw, __u16 mode) /* * CMD_SET_WMM_MODE. */ -struct mwl8k_cmd_set_wmm { +struct mwl8k_cmd_set_wmm_mode { struct mwl8k_cmd_pkt header; __le16 action; } __attribute__((packed)); -static int mwl8k_set_wmm(struct ieee80211_hw *hw, bool enable) +static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_set_wmm *cmd; + struct mwl8k_cmd_set_wmm_mode *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2219,28 +2605,29 @@ static int mwl8k_set_wmm(struct ieee80211_hw *hw, bool enable) } /* - * CMD_SET_RTS_THRESHOLD. + * CMD_MIMO_CONFIG. */ -struct mwl8k_cmd_rts_threshold { +struct mwl8k_cmd_mimo_config { struct mwl8k_cmd_pkt header; - __le16 action; - __le16 threshold; + __le32 action; + __u8 rx_antenna_map; + __u8 tx_antenna_map; } __attribute__((packed)); -static int mwl8k_rts_threshold(struct ieee80211_hw *hw, - u16 action, u16 threshold) +static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) { - struct mwl8k_cmd_rts_threshold *cmd; + struct mwl8k_cmd_mimo_config *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); + cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(action); - cmd->threshold = cpu_to_le16(threshold); + cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); + cmd->rx_antenna_map = rx; + cmd->tx_antenna_map = tx; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2249,64 +2636,81 @@ static int mwl8k_rts_threshold(struct ieee80211_hw *hw, } /* - * CMD_SET_EDCA_PARAMS. + * CMD_USE_FIXED_RATE (STA version). */ -struct mwl8k_cmd_set_edca_params { +struct mwl8k_cmd_use_fixed_rate_sta { struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[8]; + __le32 rate_type; + __le32 reserved1; + __le32 reserved2; +} __attribute__((packed)); - /* See MWL8K_SET_EDCA_XXX below */ - __le16 action; - - /* TX opportunity in units of 32 us */ - __le16 txop; +#define MWL8K_USE_AUTO_RATE 0x0002 +#define MWL8K_UCAST_RATE 0 - /* Log exponent of max contention period: 0...15*/ - __u8 log_cw_max; +static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) +{ + struct mwl8k_cmd_use_fixed_rate_sta *cmd; + int rc; - /* Log exponent of min contention period: 0...15 */ - __u8 log_cw_min; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; - /* Adaptive interframe spacing in units of 32us */ - __u8 aifs; + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); - /* TX queue to configure */ - __u8 txq; -} __attribute__((packed)); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); -#define MWL8K_SET_EDCA_CW 0x01 -#define MWL8K_SET_EDCA_TXOP 0x02 -#define MWL8K_SET_EDCA_AIFS 0x04 + return rc; +} -#define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \ - MWL8K_SET_EDCA_TXOP | \ - MWL8K_SET_EDCA_AIFS) +/* + * CMD_USE_FIXED_RATE (AP version). + */ +struct mwl8k_cmd_use_fixed_rate_ap { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct mwl8k_rate_entry_ap { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[4]; + u8 multicast_rate; + u8 multicast_rate_type; + u8 management_rate; +} __attribute__((packed)); static int -mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, - __u16 cw_min, __u16 cw_max, - __u8 aifs, __u16 txop) +mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) { - struct mwl8k_cmd_set_edca_params *cmd; + struct mwl8k_cmd_use_fixed_rate_ap *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - /* - * Queues 0 (BE) and 1 (BK) are swapped in hardware for - * this call. - */ - qnum ^= !(qnum >> 1); - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); - cmd->txop = cpu_to_le16(txop); - cmd->log_cw_max = (u8)ilog2(cw_max + 1); - cmd->log_cw_min = (u8)ilog2(cw_min + 1); - cmd->aifs = aifs; - cmd->txq = qnum; + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->multicast_rate = mcast; + cmd->management_rate = mgmt; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2315,314 +2719,371 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, } /* - * CMD_FINALIZE_JOIN. + * CMD_ENABLE_SNIFFER. */ - -/* FJ beacon buffer size is compiled into the firmware. */ -#define MWL8K_FJ_BEACON_MAXLEN 128 - -struct mwl8k_cmd_finalize_join { +struct mwl8k_cmd_enable_sniffer { struct mwl8k_cmd_pkt header; - __le32 sleep_interval; /* Number of beacon periods to sleep */ - __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; + __le32 action; } __attribute__((packed)); -static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, - __u16 framelen, __u16 dtim) +static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable) { - struct mwl8k_cmd_finalize_join *cmd; - struct ieee80211_mgmt *payload = frame; - u16 hdrlen; - u32 payload_len; + struct mwl8k_cmd_enable_sniffer *cmd; int rc; - if (frame == NULL) - return -EINVAL; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); + cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); + cmd->action = cpu_to_le32(!!enable); - hdrlen = ieee80211_hdrlen(payload->frame_control); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); - payload_len = framelen > hdrlen ? framelen - hdrlen : 0; + return rc; +} - /* XXX TBD Might just have to abort and return an error */ - if (payload_len > MWL8K_FJ_BEACON_MAXLEN) - printk(KERN_ERR "%s(): WARNING: Incomplete beacon " - "sent to firmware. Sz=%u MAX=%u\n", __func__, - payload_len, MWL8K_FJ_BEACON_MAXLEN); +/* + * CMD_SET_MAC_ADDR. + */ +struct mwl8k_cmd_set_mac_addr { + struct mwl8k_cmd_pkt header; + union { + struct { + __le16 mac_type; + __u8 mac_addr[ETH_ALEN]; + } mbss; + __u8 mac_addr[ETH_ALEN]; + }; +} __attribute__((packed)); - if (payload_len > MWL8K_FJ_BEACON_MAXLEN) - payload_len = MWL8K_FJ_BEACON_MAXLEN; +#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 - if (payload && payload_len) - memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); +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; - rc = mwl8k_post_cmd(hw, &cmd->header); + 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(mac_type); + memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); + } else { + memcpy(cmd->mac_addr, mac, ETH_ALEN); + } + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); + return rc; } /* - * CMD_UPDATE_STADB. + * CMD_SET_RATEADAPT_MODE. */ -struct mwl8k_cmd_update_sta_db { +struct mwl8k_cmd_set_rate_adapt_mode { struct mwl8k_cmd_pkt header; + __le16 action; + __le16 mode; +} __attribute__((packed)); - /* See STADB_ACTION_TYPE */ - __le32 action; +static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) +{ + struct mwl8k_cmd_set_rate_adapt_mode *cmd; + int rc; - /* Peer MAC address */ - __u8 peer_addr[ETH_ALEN]; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; - __le32 reserved; + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->mode = cpu_to_le16(mode); - /* Peer info - valid during add/update. */ - struct peer_capability_info peer_info; + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_BSS_START. + */ +struct mwl8k_cmd_bss_start { + struct mwl8k_cmd_pkt header; + __le32 enable; } __attribute__((packed)); -static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, __u32 action) +static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int enable) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - struct ieee80211_bss_conf *info = &mv_vif->bss_info; - struct mwl8k_cmd_update_sta_db *cmd; - struct peer_capability_info *peer_info; - struct ieee80211_rate *bitrates = mv_vif->legacy_rates; + struct mwl8k_cmd_bss_start *cmd; int rc; - __u8 count, *rates; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->enable = cpu_to_le32(enable); - cmd->action = cpu_to_le32(action); - peer_info = &cmd->peer_info; - memcpy(cmd->peer_addr, mv_vif->bssid, ETH_ALEN); - - switch (action) { - case MWL8K_STA_DB_ADD_ENTRY: - case MWL8K_STA_DB_MODIFY_ENTRY: - /* Build peer_info block */ - peer_info->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; - peer_info->basic_caps = cpu_to_le16(info->assoc_capability); - peer_info->interop = 1; - peer_info->amsdu_enabled = 0; - - rates = peer_info->legacy_rates; - for (count = 0; count < mv_vif->legacy_nrates; count++) - rates[count] = bitrates[count].hw_value; - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = peer_info->station_id; - - break; - - case MWL8K_STA_DB_DEL_ENTRY: - case MWL8K_STA_DB_FLUSH: - default: - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = 0; - break; - } + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* - * CMD_SET_AID. + * CMD_SET_NEW_STN. */ -#define MWL8K_RATE_INDEX_MAX_ARRAY 14 - -#define MWL8K_FRAME_PROT_DISABLED 0x00 -#define MWL8K_FRAME_PROT_11G 0x07 -#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 -#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 - -struct mwl8k_cmd_update_set_aid { - struct mwl8k_cmd_pkt header; - __le16 aid; - - /* AP's MAC address (BSSID) */ - __u8 bssid[ETH_ALEN]; - __le16 protection_mode; - __u8 supp_rates[MWL8K_RATE_INDEX_MAX_ARRAY]; +struct mwl8k_cmd_set_new_stn { + struct mwl8k_cmd_pkt header; + __le16 aid; + __u8 mac_addr[6]; + __le16 stn_id; + __le16 action; + __le16 rsvd; + __le32 legacy_rates; + __u8 ht_rates[4]; + __le16 cap_info; + __le16 ht_capabilities_info; + __u8 mac_ht_param_info; + __u8 rev; + __u8 control_channel; + __u8 add_channel; + __le16 op_mode; + __le16 stbc; + __u8 add_qos_info; + __u8 is_qos_sta; + __le32 fw_sta_ptr; } __attribute__((packed)); -static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +#define MWL8K_STA_ACTION_ADD 0 +#define MWL8K_STA_ACTION_REMOVE 2 + +static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - struct ieee80211_bss_conf *info = &mv_vif->bss_info; - struct mwl8k_cmd_update_set_aid *cmd; - struct ieee80211_rate *bitrates = mv_vif->legacy_rates; - int count; - u16 prot_mode; + struct mwl8k_cmd_set_new_stn *cmd; + u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->aid = cpu_to_le16(info->aid); - - memcpy(cmd->bssid, mv_vif->bssid, ETH_ALEN); - - if (info->use_cts_prot) { - prot_mode = MWL8K_FRAME_PROT_11G; - } else { - switch (info->ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION) { - case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: - prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; - break; - case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; - break; - default: - prot_mode = MWL8K_FRAME_PROT_DISABLED; - break; - } + cmd->aid = cpu_to_le16(sta->aid); + 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); + 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->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; + cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; + cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); + cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + cmd->is_qos_sta = 1; } - cmd->protection_mode = cpu_to_le16(prot_mode); - - for (count = 0; count < mv_vif->legacy_nrates; count++) - cmd->supp_rates[count] = bitrates[count].hw_value; - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } -/* - * CMD_SET_RATE. - */ -struct mwl8k_cmd_update_rateset { - struct mwl8k_cmd_pkt header; - __u8 legacy_rates[MWL8K_RATE_INDEX_MAX_ARRAY]; +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; - /* Bitmap for supported MCS codes. */ - __u8 mcs_set[MWL8K_IEEE_LEGACY_DATA_RATES]; - __u8 reserved[MWL8K_IEEE_LEGACY_DATA_RATES]; -} __attribute__((packed)); + 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_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); -static int mwl8k_update_rateset(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + return rc; +} + +static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - struct mwl8k_cmd_update_rateset *cmd; - struct ieee80211_rate *bitrates = mv_vif->legacy_rates; - int count; + 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_RATE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); - for (count = 0; count < mv_vif->legacy_nrates; count++) - cmd->legacy_rates[count] = bitrates[count].hw_value; - - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; } /* - * CMD_USE_FIXED_RATE. + * CMD_UPDATE_STADB. */ -#define MWL8K_RATE_TABLE_SIZE 8 -#define MWL8K_UCAST_RATE 0 -#define MWL8K_USE_AUTO_RATE 0x0002 +struct ewc_ht_info { + __le16 control1; + __le16 control2; + __le16 control3; +} __attribute__((packed)); + +struct peer_capability_info { + /* Peer type - AP vs. STA. */ + __u8 peer_type; + + /* Basic 802.11 capabilities from assoc resp. */ + __le16 basic_caps; -struct mwl8k_rate_entry { - /* Set to 1 if HT rate, 0 if legacy. */ - __le32 is_ht_rate; + /* Set if peer supports 802.11n high throughput (HT). */ + __u8 ht_support; - /* Set to 1 to use retry_count field. */ - __le32 enable_retry; + /* Valid if HT is supported. */ + __le16 ht_caps; + __u8 extended_ht_caps; + struct ewc_ht_info ewc_info; - /* Specified legacy rate or MCS. */ - __le32 rate; + /* Legacy rate table. Intersection of our rates and peer rates. */ + __u8 legacy_rates[12]; - /* Number of allowed retries. */ - __le32 retry_count; -} __attribute__((packed)); + /* HT rate table. Intersection of our rates and peer rates. */ + __u8 ht_rates[16]; + __u8 pad[16]; -struct mwl8k_rate_table { - /* 1 to allow specified rate and below */ - __le32 allow_rate_drop; - __le32 num_rates; - struct mwl8k_rate_entry rate_entry[MWL8K_RATE_TABLE_SIZE]; + /* If set, interoperability mode, no proprietary extensions. */ + __u8 interop; + __u8 pad2; + __u8 station_id; + __le16 amsdu_enabled; } __attribute__((packed)); -struct mwl8k_cmd_use_fixed_rate { - struct mwl8k_cmd_pkt header; +struct mwl8k_cmd_update_stadb { + struct mwl8k_cmd_pkt header; + + /* See STADB_ACTION_TYPE */ __le32 action; - struct mwl8k_rate_table rate_table; - /* Unicast, Broadcast or Multicast */ - __le32 rate_type; - __le32 reserved1; - __le32 reserved2; + /* Peer MAC address */ + __u8 peer_addr[ETH_ALEN]; + + __le32 reserved; + + /* Peer info - valid during add/update. */ + struct peer_capability_info peer_info; } __attribute__((packed)); -static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, - u32 action, u32 rate_type, struct mwl8k_rate_table *rate_table) +#define MWL8K_STA_DB_MODIFY_ENTRY 1 +#define MWL8K_STA_DB_DEL_ENTRY 2 + +/* Peer Entry flags - used to define the type of the peer node */ +#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + +static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { - struct mwl8k_cmd_use_fixed_rate *cmd; - int count; + struct mwl8k_cmd_update_stadb *cmd; + struct peer_capability_info *p; + u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); + memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); + + p = &cmd->peer_info; + p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; + p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); + p->ht_support = sta->ht_cap.ht_supported; + p->ht_caps = sta->ht_cap.cap; + p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + 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; - cmd->action = cpu_to_le32(action); - cmd->rate_type = cpu_to_le32(rate_type); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); - if (rate_table != NULL) { - /* - * Copy over each field manually so that endian - * conversion can be done. - */ - cmd->rate_table.allow_rate_drop = - cpu_to_le32(rate_table->allow_rate_drop); - cmd->rate_table.num_rates = - cpu_to_le32(rate_table->num_rates); - - for (count = 0; count < rate_table->num_rates; count++) { - struct mwl8k_rate_entry *dst = - &cmd->rate_table.rate_entry[count]; - struct mwl8k_rate_entry *src = - &rate_table->rate_entry[count]; - - dst->is_ht_rate = cpu_to_le32(src->is_ht_rate); - dst->enable_retry = cpu_to_le32(src->enable_retry); - dst->rate = cpu_to_le32(src->rate); - dst->retry_count = cpu_to_le32(src->retry_count); - } - } + return rc ? rc : p->station_id; +} + +static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_update_stadb *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); + memcpy(cmd->peer_addr, addr, ETH_ALEN); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2641,31 +3102,81 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) u32 status; status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (!status) return IRQ_NONE; - if (status & MWL8K_A2H_INT_TX_DONE) - tasklet_schedule(&priv->tx_reclaim_task); + if (status & MWL8K_A2H_INT_TX_DONE) { + status &= ~MWL8K_A2H_INT_TX_DONE; + tasklet_schedule(&priv->poll_tx_task); + } + + if (status & MWL8K_A2H_INT_RX_READY) { + status &= ~MWL8K_A2H_INT_RX_READY; + tasklet_schedule(&priv->poll_rx_task); + } + + if (status) + iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + + if (status & MWL8K_A2H_INT_OPC_DONE) { + if (priv->hostcmd_wait != NULL) + complete(priv->hostcmd_wait); + } + + if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { + if (!mutex_is_locked(&priv->fw_mutex) && + priv->radio_on && priv->pending_tx_pkts) + mwl8k_tx_start(priv); + } + + return IRQ_HANDLED; +} + +static void mwl8k_tx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + int i; + + limit = 32; + + spin_lock_bh(&priv->tx_lock); + + for (i = 0; i < MWL8K_TX_QUEUES; i++) + limit -= mwl8k_txq_reclaim(hw, i, limit, 0); + + if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { + complete(priv->tx_wait); + priv->tx_wait = NULL; + } + + spin_unlock_bh(&priv->tx_lock); - if (status & MWL8K_A2H_INT_RX_READY) { - while (rxq_process(hw, 0, 1)) - rxq_refill(hw, 0, 1); + if (limit) { + writel(~MWL8K_A2H_INT_TX_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_tx_task); } +} - if (status & MWL8K_A2H_INT_OPC_DONE) { - if (priv->hostcmd_wait != NULL) - complete(priv->hostcmd_wait); - } +static void mwl8k_rx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; - if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { - if (!mutex_is_locked(&priv->fw_mutex) && - priv->radio_on && priv->pending_tx_pkts) - mwl8k_tx_start(priv); - } + limit = 32; + limit -= rxq_process(hw, 0, limit); + limit -= rxq_refill(hw, 0, limit); - return IRQ_HANDLED; + if (limit) { + writel(~MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_rx_task); + } } @@ -2678,7 +3189,7 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) int index = skb_get_queue_mapping(skb); int rc; - if (priv->current_channel == NULL) { + if (!priv->radio_on) { printk(KERN_DEBUG "%s: dropped TX frame since radio " "disabled\n", wiphy_name(hw->wiphy)); dev_kfree_skb(skb); @@ -2695,7 +3206,7 @@ static int mwl8k_start(struct ieee80211_hw *hw) struct mwl8k_priv *priv = hw->priv; int rc; - rc = request_irq(priv->pdev->irq, &mwl8k_interrupt, + rc = request_irq(priv->pdev->irq, mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { printk(KERN_ERR "%s: failed to register IRQ handler\n", @@ -2703,31 +3214,34 @@ static int mwl8k_start(struct ieee80211_hw *hw) return -EIO; } - /* Enable tx reclaim tasklet */ - tasklet_enable(&priv->tx_reclaim_task); + /* Enable TX reclaim and RX tasklets. */ + tasklet_enable(&priv->poll_tx_task); + tasklet_enable(&priv->poll_rx_task); /* Enable interrupts */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); rc = mwl8k_fw_lock(hw); if (!rc) { - rc = mwl8k_cmd_802_11_radio_enable(hw); + rc = mwl8k_cmd_radio_enable(hw); - if (!rc) - rc = mwl8k_cmd_set_pre_scan(hw); + if (!priv->ap_fw) { + if (!rc) + rc = mwl8k_cmd_enable_sniffer(hw, 0); - if (!rc) - rc = mwl8k_cmd_set_post_scan(hw, - "\x00\x00\x00\x00\x00\x00"); + if (!rc) + rc = mwl8k_cmd_set_pre_scan(hw); - if (!rc) - rc = mwl8k_cmd_setrateadaptmode(hw, 0); + if (!rc) + rc = mwl8k_cmd_set_post_scan(hw, + "\x00\x00\x00\x00\x00\x00"); + } if (!rc) - rc = mwl8k_set_wmm(hw, 0); + rc = mwl8k_cmd_set_rateadapt_mode(hw, 0); if (!rc) - rc = mwl8k_enable_sniffer(hw, 0); + rc = mwl8k_cmd_set_wmm_mode(hw, 0); mwl8k_fw_unlock(hw); } @@ -2735,7 +3249,8 @@ static int mwl8k_start(struct ieee80211_hw *hw) if (rc) { iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); - tasklet_disable(&priv->tx_reclaim_task); + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); } return rc; @@ -2746,7 +3261,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw) struct mwl8k_priv *priv = hw->priv; int i; - mwl8k_cmd_802_11_radio_disable(hw); + mwl8k_cmd_radio_disable(hw); ieee80211_stop_queues(hw); @@ -2759,36 +3274,27 @@ static void mwl8k_stop(struct ieee80211_hw *hw) if (priv->beacon_skb != NULL) dev_kfree_skb(priv->beacon_skb); - /* Stop tx reclaim tasklet */ - tasklet_disable(&priv->tx_reclaim_task); + /* Stop TX reclaim and RX tasklets. */ + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); } static int mwl8k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + 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; - - /* - * We only support managed interfaces for now. - */ - if (conf->type != NL80211_IFTYPE_STATION) - return -EINVAL; + u32 macids_supported; + int macid; /* * 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 " @@ -2797,42 +3303,54 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, return -EINVAL; } - /* Clean out driver private area */ - mwl8k_vif = MWL8K_VIF(conf->vif); - memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); - - /* Set and save the mac address */ - mwl8k_set_mac_addr(hw, conf->mac_addr); - memcpy(mwl8k_vif->mac_addr, conf->mac_addr, ETH_ALEN); - /* Back pointer to parent config block */ - mwl8k_vif->priv = priv; + 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; + } - /* Setup initial PHY parameters */ - memcpy(mwl8k_vif->legacy_rates, - priv->rates, sizeof(mwl8k_vif->legacy_rates)); - mwl8k_vif->legacy_nrates = ARRAY_SIZE(priv->rates); + macid = ffs(macids_supported & ~priv->macids_used); + if (!macid--) + return -EBUSY; - /* Set Initial sequence number to zero */ + /* Setup driver private area. */ + mwl8k_vif = MWL8K_VIF(vif); + memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); + mwl8k_vif->vif = vif; + mwl8k_vif->macid = macid; mwl8k_vif->seqno = 0; - priv->vif = conf->vif; - priv->current_channel = NULL; + /* 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; } static void mwl8k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - if (priv->vif == NULL) - return; + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); - mwl8k_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) @@ -2842,8 +3360,7 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) int rc; if (conf->flags & IEEE80211_CONF_IDLE) { - mwl8k_cmd_802_11_radio_disable(hw); - priv->current_channel = NULL; + mwl8k_cmd_radio_disable(hw); return 0; } @@ -2851,19 +3368,17 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) return rc; - rc = mwl8k_cmd_802_11_radio_enable(hw); + rc = mwl8k_cmd_radio_enable(hw); if (rc) goto out; - rc = mwl8k_cmd_set_rf_channel(hw, conf->channel); + rc = mwl8k_cmd_set_rf_channel(hw, conf); if (rc) goto out; - priv->current_channel = conf->channel; - if (conf->power_level > 18) conf->power_level = 18; - rc = mwl8k_cmd_802_11_rf_tx_power(hw, conf->power_level); + rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); if (rc) goto out; @@ -2881,82 +3396,162 @@ 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; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + u32 ap_legacy_rates; + u8 ap_mcs_rates[16]; int rc; - if (changed & BSS_CHANGED_BSSID) - memcpy(mwl8k_vif->bssid, info->bssid, ETH_ALEN); - - if ((changed & BSS_CHANGED_ASSOC) == 0) + if (mwl8k_fw_lock(hw)) return; - priv->capture_beacon = false; + /* + * No need to capture a beacon if we're no longer associated. + */ + if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) + priv->capture_beacon = false; - rc = mwl8k_fw_lock(hw); - if (rc) - return; + /* + * Get the AP's legacy and MCS rates. + */ + if (vif->bss_conf.assoc) { + struct ieee80211_sta *ap; - if (info->assoc) { - memcpy(&mwl8k_vif->bss_info, info, - sizeof(struct ieee80211_bss_conf)); + rcu_read_lock(); - /* Install rates */ - rc = mwl8k_update_rateset(hw, vif); - if (rc) + ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (ap == NULL) { + rcu_read_unlock(); goto out; + } - /* Turn on rate adaptation */ - rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, - MWL8K_UCAST_RATE, NULL); + 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(); + } + + if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { + rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates); if (rc) goto out; - /* Set radio preamble */ - rc = mwl8k_set_radio_preamble(hw, info->use_short_preamble); + rc = mwl8k_cmd_use_fixed_rate_sta(hw); if (rc) goto out; + } - /* Set slot time */ - rc = mwl8k_cmd_set_slot(hw, info->use_short_slot); + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); if (rc) goto out; + } - /* Update peer rate info */ - rc = mwl8k_cmd_update_sta_db(hw, vif, - MWL8K_STA_DB_MODIFY_ENTRY); + if (changed & BSS_CHANGED_ERP_SLOT) { + rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); if (rc) goto out; + } - /* Set AID */ - rc = mwl8k_cmd_set_aid(hw, vif); + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_HT))) { + rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); if (rc) goto out; + } + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { /* * Finalize the join. Tell rx handler to process * next beacon from our BSSID. */ - memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN); + memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); priv->capture_beacon = true; - } else { - rc = mwl8k_cmd_update_sta_db(hw, vif, MWL8K_STA_DB_DEL_ENTRY); - memset(&mwl8k_vif->bss_info, 0, - sizeof(struct ieee80211_bss_conf)); - memset(mwl8k_vif->bssid, 0, ETH_ALEN); } 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); + 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); + } + + 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, vif, skb->data, skb->len); + kfree_skb(skb); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + mwl8k_cmd_bss_start(hw, vif, 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) + struct netdev_hw_addr_list *mc_list) { struct mwl8k_cmd_pkt *cmd; @@ -2967,7 +3562,7 @@ static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, * 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; } @@ -2984,7 +3579,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, * 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", @@ -2993,7 +3588,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, } if (!priv->sniffer_enabled) { - if (mwl8k_enable_sniffer(hw, 1)) + if (mwl8k_cmd_enable_sniffer(hw, 1)) return 0; priv->sniffer_enabled = true; } @@ -3005,6 +3600,14 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, 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, @@ -3036,11 +3639,13 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, /* Clear unsupported feature flags */ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; - if (mwl8k_fw_lock(hw)) + if (mwl8k_fw_lock(hw)) { + kfree(cmd); return; + } if (priv->sniffer_enabled) { - mwl8k_enable_sniffer(hw, 0); + mwl8k_cmd_enable_sniffer(hw, 0); priv->sniffer_enabled = false; } @@ -3051,7 +3656,8 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ mwl8k_cmd_set_pre_scan(hw); } else { - u8 *bssid; + struct mwl8k_vif *mwl8k_vif; + const u8 *bssid; /* * Enable the BSS filter. @@ -3061,9 +3667,11 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, * (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 = MWL8K_VIF(priv->vif)->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); } @@ -3077,7 +3685,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ 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) { @@ -3090,7 +3698,39 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - return mwl8k_rts_threshold(hw, MWL8K_CMD_SET, value); + return mwl8k_cmd_set_rts_threshold(hw, value); +} + +static int mwl8k_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + + 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 int mwl8k_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + int ret; + + if (!priv->ap_fw) { + ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); + if (ret >= 0) { + MWL8K_STA(sta)->peer_id = ret; + return 0; + } + + return ret; + } + + return mwl8k_cmd_set_new_stn_add(hw, vif, sta); } static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, @@ -3102,14 +3742,14 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, rc = mwl8k_fw_lock(hw); if (!rc) { if (!priv->wmm_enabled) - rc = mwl8k_set_wmm(hw, 1); + rc = mwl8k_cmd_set_wmm_mode(hw, 1); if (!rc) - rc = mwl8k_set_edca_params(hw, queue, - params->cw_min, - params->cw_max, - params->aifs, - params->txop); + rc = mwl8k_cmd_set_edca_params(hw, queue, + params->cw_min, + params->cw_max, + params->aifs, + params->txop); mwl8k_fw_unlock(hw); } @@ -3117,28 +3757,26 @@ 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) +static int mwl8k_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_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; + return mwl8k_cmd_get_stat(hw, stats); } -static int mwl8k_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) +static int +mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - return mwl8k_cmd_802_11_get_stat(hw, stats); + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) + return -ENOTSUPP; + return 0; + default: + return -ENOTSUPP; + } } static const struct ieee80211_ops mwl8k_ops = { @@ -3152,59 +3790,75 @@ static const struct ieee80211_ops mwl8k_ops = { .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, + .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, }; -static void mwl8k_tx_reclaim_handler(unsigned long data) -{ - int i; - struct ieee80211_hw *hw = (struct ieee80211_hw *) data; - struct mwl8k_priv *priv = hw->priv; - - spin_lock_bh(&priv->tx_lock); - for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 0); - - if (priv->tx_wait != NULL && !priv->pending_tx_pkts) { - complete(priv->tx_wait); - priv->tx_wait = NULL; - } - spin_unlock_bh(&priv->tx_lock); -} - static void mwl8k_finalize_join_worker(struct work_struct *work) { struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, finalize_join_worker); struct sk_buff *skb = priv->beacon_skb; - u8 dtim = MWL8K_VIF(priv->vif)->bss_info.dtim_period; + 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_finalize_join(priv->hw, skb->data, skb->len, dtim); - 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; } -static struct mwl8k_device_info di_8687 = { - .part_name = "88w8687", - .helper_image = "mwl8k/helper_8687.fw", - .fw_image = "mwl8k/fmimage_8687.fw", - .rxd_ops = &rxd_8687_ops, - .modes = BIT(NL80211_IFTYPE_STATION), +enum { + MWL8363 = 0, + MWL8687, + MWL8366, }; -static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { - { - PCI_VDEVICE(MARVELL, 0x2a2b), - .driver_data = (unsigned long)&di_8687, - }, { - PCI_VDEVICE(MARVELL, 0x2a30), - .driver_data = (unsigned long)&di_8687, - }, { +static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { + [MWL8363] = { + .part_name = "88w8363", + .helper_image = "mwl8k/helper_8363.fw", + .fw_image = "mwl8k/fmimage_8363.fw", + }, + [MWL8687] = { + .part_name = "88w8687", + .helper_image = "mwl8k/helper_8687.fw", + .fw_image = "mwl8k/fmimage_8687.fw", + }, + [MWL8366] = { + .part_name = "88w8366", + .helper_image = "mwl8k/helper_8366.fw", + .fw_image = "mwl8k/fmimage_8366.fw", + .ap_rxd_ops = &rxd_8366_ap_ops, }, }; + +MODULE_FIRMWARE("mwl8k/helper_8363.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); +MODULE_FIRMWARE("mwl8k/helper_8687.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); +MODULE_FIRMWARE("mwl8k/helper_8366.fw"); +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, }, + { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, + { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, + { }, +}; MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); static int __devinit mwl8k_probe(struct pci_dev *pdev, @@ -3221,6 +3875,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, printed_version = 1; } + rc = pci_enable_device(pdev); if (rc) { printk(KERN_ERR "%s: Cannot enable new PCI device\n", @@ -3232,11 +3887,12 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot obtain PCI resources\n", MWL8K_NAME); - return rc; + goto err_disable_device; } pci_set_master(pdev); + hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops); if (hw == NULL) { printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME); @@ -3244,17 +3900,14 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_free_reg; } + SET_IEEE80211_DEV(hw, &pdev->dev); + pci_set_drvdata(pdev, hw); + priv = hw->priv; priv->hw = hw; priv->pdev = pdev; - priv->device_info = (void *)id->driver_data; - priv->rxd_ops = priv->device_info->rxd_ops; - priv->sniffer_enabled = false; - priv->wmm_enabled = false; - priv->pending_tx_pkts = 0; + priv->device_info = &mwl8k_info_tbl[id->driver_data]; - SET_IEEE80211_DEV(hw, &pdev->dev); - pci_set_drvdata(pdev, hw); priv->sram = pci_iomap(pdev, 0, 0x10000); if (priv->sram == NULL) { @@ -3277,16 +3930,46 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, } } - memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels)); - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(mwl8k_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(mwl8k_rates); - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(mwl8k_rates)); - memcpy(priv->rates, mwl8k_rates, sizeof(mwl8k_rates)); + /* Reset firmware and hardware */ + mwl8k_hw_reset(priv); + + /* Ask userland hotplug daemon for the device firmware */ + rc = mwl8k_request_firmware(priv); + if (rc) { + printk(KERN_ERR "%s: Firmware files not found\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + + /* Load firmware into hardware */ + rc = mwl8k_load_firmware(hw); + if (rc) { + printk(KERN_ERR "%s: Cannot start firmware\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + + /* Reclaim memory once firmware is successfully loaded */ + mwl8k_release_firmware(priv); + + + if (priv->ap_fw) { + priv->rxd_ops = priv->device_info->ap_rxd_ops; + if (priv->rxd_ops == NULL) { + printk(KERN_ERR "%s: Driver does not have AP " + "firmware image support for this hardware\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + } else { + priv->rxd_ops = &rxd_sta_ops; + } + + priv->sniffer_enabled = false; + priv->wmm_enabled = false; + priv->pending_tx_pkts = 0; + /* * Extra headroom is the size of the required DMA header @@ -3299,12 +3982,13 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, hw->queues = MWL8K_TX_QUEUES; - hw->wiphy->interface_modes = priv->device_info->modes; - - /* 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); - priv->vif = NULL; + hw->sta_data_size = sizeof(struct mwl8k_sta); + + priv->macids_used = 0; + INIT_LIST_HEAD(&priv->vif_list); /* Set default radio state and preamble */ priv->radio_on = 0; @@ -3313,19 +3997,20 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); - /* TX reclaim tasklet */ - tasklet_init(&priv->tx_reclaim_task, - mwl8k_tx_reclaim_handler, (unsigned long)hw); - tasklet_disable(&priv->tx_reclaim_task); + /* TX reclaim and RX tasklets. */ + tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_tx_task); + tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_rx_task); /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); if (priv->cookie == NULL) - goto err_iounmap; + goto err_stop_firmware; rc = mwl8k_rxq_init(hw, 0); if (rc) - goto err_iounmap; + goto err_free_cookie; rxq_refill(hw, 0, INT_MAX); mutex_init(&priv->fw_mutex); @@ -3345,10 +4030,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); + iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - rc = request_irq(priv->pdev->irq, &mwl8k_interrupt, + rc = request_irq(priv->pdev->irq, mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { printk(KERN_ERR "%s: failed to register IRQ handler\n", @@ -3356,31 +4042,9 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_free_queues; } - /* Reset firmware and hardware */ - mwl8k_hw_reset(priv); - - /* Ask userland hotplug daemon for the device firmware */ - rc = mwl8k_request_firmware(priv); - if (rc) { - printk(KERN_ERR "%s: Firmware files not found\n", - wiphy_name(hw->wiphy)); - goto err_free_irq; - } - - /* Load firmware into hardware */ - rc = mwl8k_load_firmware(hw); - if (rc) { - printk(KERN_ERR "%s: Cannot start firmware\n", - wiphy_name(hw->wiphy)); - goto err_stop_firmware; - } - - /* Reclaim memory once firmware is successfully loaded */ - mwl8k_release_firmware(priv); - /* * Temporarily enable interrupts. Initial firmware host - * commands use interrupts and avoids polling. Disable + * commands use interrupts and avoid polling. Disable * interrupts when done. */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); @@ -3396,22 +4060,29 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot initialise firmware\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + 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_802_11_radio_disable(hw); + rc = mwl8k_cmd_radio_disable(hw); if (rc) { printk(KERN_ERR "%s: Cannot disable\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + goto err_free_irq; } /* Clear MAC address */ - rc = mwl8k_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)); - goto err_stop_firmware; + goto err_free_irq; } /* Disable interrupts */ @@ -3422,7 +4093,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot register device\n", wiphy_name(hw->wiphy)); - goto err_stop_firmware; + goto err_free_queues; } printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n", @@ -3434,10 +4105,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, return 0; -err_stop_firmware: - mwl8k_hw_reset(priv); - mwl8k_release_firmware(priv); - err_free_irq: iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); @@ -3447,11 +4114,16 @@ err_free_queues: mwl8k_txq_deinit(hw, i); mwl8k_rxq_deinit(hw, 0); -err_iounmap: +err_free_cookie: if (priv->cookie != NULL) pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); +err_stop_firmware: + mwl8k_hw_reset(priv); + mwl8k_release_firmware(priv); + +err_iounmap: if (priv->regs != NULL) pci_iounmap(pdev, priv->regs); @@ -3463,6 +4135,8 @@ err_iounmap: err_free_reg: pci_release_regions(pdev); + +err_disable_device: pci_disable_device(pdev); return rc; @@ -3487,15 +4161,16 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) ieee80211_unregister_hw(hw); - /* Remove tx reclaim tasklet */ - tasklet_kill(&priv->tx_reclaim_task); + /* Remove TX reclaim and RX tasklets. */ + tasklet_kill(&priv->poll_tx_task); + tasklet_kill(&priv->poll_rx_task); /* Stop hardware */ mwl8k_hw_reset(priv); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_deinit(hw, i);