diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
34 files changed, 4571 insertions, 1717 deletions
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index e092af09d6b..99310c03325 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -9,6 +9,9 @@ config IWLWIFI config IWLWIFI_LEDS bool "Enable LED support in iwlagn and iwl3945 drivers" depends on IWLWIFI + default y + ---help--- + Select this if you want LED support. config IWLWIFI_SPECTRUM_MEASUREMENT bool "Enable Spectrum Measurement in iwlagn driver" diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 7da52f1cc1d..a95caa01414 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -46,7 +46,7 @@ #include "iwl-5000-hw.h" /* Highest firmware API version supported */ -#define IWL1000_UCODE_API_MAX 2 +#define IWL1000_UCODE_API_MAX 3 /* Lowest firmware API version supported */ #define IWL1000_UCODE_API_MIN 1 @@ -55,19 +55,109 @@ #define _IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE #api ".ucode" #define IWL1000_MODULE_FIRMWARE(api) _IWL1000_MODULE_FIRMWARE(api) + +/* + * For 1000, use advance thermal throttling critical temperature threshold, + * but legacy thermal management implementation for now. + * This is for the reason of 1000 uCode using advance thermal throttling API + * but not implement ct_kill_exit based on ct_kill exit temperature + * so the thermal throttling will still based on legacy thermal throttling + * management. + * The code here need to be modified once 1000 uCode has the advanced thermal + * throttling algorithm in place + */ +static void iwl1000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 1000 series */ +static void iwl1000_nic_config(struct iwl_priv *priv) +{ + iwl5000_nic_config(priv); + + /* Setting digital SVR for 1000 card to 1.32V */ + /* locking is acquired in iwl_set_bits_mask_prph() function */ + iwl_set_bits_mask_prph(priv, APMG_DIGITAL_SVR_REG, + APMG_SVR_DIGITAL_VOLTAGE_1_32, + ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); +} + +static struct iwl_lib_ops iwl1000_lib = { + .set_hw_params = iwl5000_hw_set_hw_params, + .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, + .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, + .txq_set_sched = iwl5000_txq_set_sched, + .txq_agg_enable = iwl5000_txq_agg_enable, + .txq_agg_disable = iwl5000_txq_agg_disable, + .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = iwl_hw_txq_free_tfd, + .txq_init = iwl_hw_tx_queue_init, + .rx_handler_setup = iwl5000_rx_handler_setup, + .setup_deferred_work = iwl5000_setup_deferred_work, + .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .load_ucode = iwl5000_load_ucode, + .init_alive_start = iwl5000_init_alive_start, + .alive_notify = iwl5000_alive_notify, + .send_tx_power = iwl5000_send_tx_power, + .update_chain_flags = iwl_update_chain_flags, + .apm_ops = { + .init = iwl5000_apm_init, + .reset = iwl5000_apm_reset, + .stop = iwl5000_apm_stop, + .config = iwl1000_nic_config, + .set_pwr_src = iwl_set_pwr_src, + }, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_5000_REG_BAND_1_CHANNELS, + EEPROM_5000_REG_BAND_2_CHANNELS, + EEPROM_5000_REG_BAND_3_CHANNELS, + EEPROM_5000_REG_BAND_4_CHANNELS, + EEPROM_5000_REG_BAND_5_CHANNELS, + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS + }, + .verify_signature = iwlcore_eeprom_verify_signature, + .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, + .release_semaphore = iwlcore_eeprom_release_semaphore, + .calib_version = iwl5000_eeprom_calib_version, + .query_addr = iwl5000_eeprom_query_addr, + }, + .post_associate = iwl_post_associate, + .isr = iwl_isr_ict, + .config_ap = iwl_config_ap, + .temp_ops = { + .temperature = iwl5000_temperature, + .set_ct_kill = iwl1000_set_ct_threshold, + }, +}; + +static struct iwl_ops iwl1000_ops = { + .ucode = &iwl5000_ucode, + .lib = &iwl1000_lib, + .hcmd = &iwl5000_hcmd, + .utils = &iwl5000_hcmd_utils, +}; + struct iwl_cfg iwl1000_bgn_cfg = { .name = "1000 Series BGN", .fw_name_pre = IWL1000_FW_PRE, .ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_min = IWL1000_UCODE_API_MIN, .sku = IWL_SKU_G|IWL_SKU_N, - .ops = &iwl5000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .ops = &iwl1000_ops, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_A, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .max_ll_items = OTP_MAX_LL_ITEMS_1000, + .shadow_ram_support = false, + .ht_greenfield_support = true, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 73f93a0ff2d..16772780c5b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -176,7 +176,7 @@ struct iwl3945_eeprom { * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * - * Entries immediately below are for 20 MHz channel width. FAT (40 MHz) + * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) * channels (only for 4965, not supported by 3945) appear later in the EEPROM. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -232,9 +232,8 @@ struct iwl3945_eeprom { #define PCI_CFG_REV_ID_BIT_BASIC_SKU (0x40) /* bit 6 */ #define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */ -#define TFD_QUEUE_MIN 0 -#define TFD_QUEUE_MAX 5 /* 4 DATA + 1 CMD */ - +/* 4 DATA + 1 CMD. There are 2 HCCA queues that are not used. */ +#define IWL39_NUM_QUEUES 5 #define IWL_NUM_SCAN_RATES (2) #define IWL_DEFAULT_TX_RETRY 15 @@ -280,8 +279,6 @@ struct iwl3945_eeprom { /* Size of uCode instruction memory in bootstrap state machine */ #define IWL39_MAX_BSM_SIZE IWL39_RTC_INST_SIZE -#define IWL39_MAX_NUM_QUEUES 8 - static inline int iwl3945_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IWL39_RTC_DATA_LOWER_BOUND) && diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index 225e5f88934..8c29ded7d02 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -79,11 +79,10 @@ static const struct { #define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /*Exclude Solid on*/ #define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1) -static int iwl3945_led_cmd_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, - struct sk_buff *skb) +static void iwl3945_led_cmd_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { - return 1; } static inline int iwl3945_brightness_to_idx(enum led_brightness brightness) @@ -99,8 +98,8 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, .id = REPLY_LEDS_CMD, .len = sizeof(struct iwl_led_cmd), .data = led_cmd, - .meta.flags = CMD_ASYNC, - .meta.u.callback = iwl3945_led_cmd_callback, + .flags = CMD_ASYNC, + .callback = iwl3945_led_cmd_callback, }; return iwl_send_cmd(priv, &cmd); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index 5eb538d18a8..a16bd4147ea 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -673,33 +673,17 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, s8 scale_action = 0; unsigned long flags; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u16 fc; - u16 rate_mask = 0; + u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0; s8 max_rate_idx = -1; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); IWL_DEBUG_RATE(priv, "enter\n"); - if (sta) - rate_mask = sta->supp_rates[sband->band]; - - /* Send management frames and NO_ACK data using lowest rate. */ - fc = le16_to_cpu(hdr->frame_control); - if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || - info->flags & IEEE80211_TX_CTL_NO_ACK || - !sta || !priv_sta) { - IWL_DEBUG_RATE(priv, "leave: No STA priv data to update!\n"); - if (!rate_mask) - info->control.rates[0].idx = - rate_lowest_index(sband, NULL); - else - info->control.rates[0].idx = - rate_lowest_index(sband, sta); - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - info->control.rates[0].count = 1; + if (rate_control_send_low(sta, priv_sta, txrc)) return; - } + + rate_mask = sta->supp_rates[sband->band]; /* get user max rate if set */ max_rate_idx = txrc->max_rate_idx; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 46288e72488..e9a685d8e3a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -349,12 +349,13 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv, * *****************************************************************************/ -void iwl3945_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +void iwl3945_hw_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = (void *)rxb->skb->data; IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(struct iwl3945_notif_statistics), - le32_to_cpu(pkt->len)); + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); memcpy(&priv->statistics_39, pkt->u.raw, sizeof(priv->statistics_39)); @@ -509,7 +510,7 @@ static void iwl3945_dbg_report_frame(struct iwl_priv *priv, struct iwl_rx_packet *pkt, struct ieee80211_hdr *header, int group100) { - if (priv->debug_level & IWL_DL_RX) + if (iwl_get_debug_level(priv) & IWL_DL_RX) _iwl3945_dbg_report_frame(priv, pkt, header, group100); } @@ -544,9 +545,7 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv, struct ieee80211_rx_status *stats) { struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; -#ifdef CONFIG_IWLWIFI_LEDS struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IWL_RX_DATA(pkt); -#endif struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt); short len = le16_to_cpu(rx_hdr->len); @@ -577,7 +576,10 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv, if (ieee80211_is_data(hdr->frame_control)) priv->rxtxpackets += len; #endif - ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); + iwl_update_stats(priv, false, hdr->frame_control, len); + + memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats)); + ieee80211_rx_irqsafe(priv->hw, rxb->skb); rxb->skb = NULL; } @@ -678,6 +680,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, /* Set "1" to report good data frames in groups of 100 */ iwl3945_dbg_report_frame(priv, pkt, header, 1); + iwl_dbg_log_rx_data_frame(priv, le16_to_cpu(rx_hdr->len), header); if (network_packet) { priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp); @@ -748,8 +751,8 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) /* Unmap tx_cmd */ if (counter) pci_unmap_single(dev, - pci_unmap_addr(&txq->cmd[index]->meta, mapping), - pci_unmap_len(&txq->cmd[index]->meta, len), + pci_unmap_addr(&txq->meta[index], mapping), + pci_unmap_len(&txq->meta[index], len), PCI_DMA_TODEVICE); /* unmap chunks if any */ @@ -773,9 +776,11 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) * iwl3945_hw_build_tx_cmd_rate - Add rate portion to TX_CMD: * */ -void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, struct iwl_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, int sta_id, int tx_id) +void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, + int sta_id, int tx_id) { u16 hw_value = ieee80211_get_tx_rate(priv->hw, info)->hw_value; u16 rate_index = min(hw_value & 0xffff, IWL_RATE_COUNT - 1); @@ -962,7 +967,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) goto error; /* Tx queue(s) */ - for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) { + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num, @@ -1139,7 +1144,7 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv) int txq_id; /* Tx queues */ - for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) if (txq_id == IWL_CMD_QUEUE_NUM) iwl_cmd_queue_free(priv); else @@ -1155,7 +1160,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) iwl_write_prph(priv, ALM_SCD_MODE_REG, 0); /* reset TFD queues */ - for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) { + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0); iwl_poll_direct_bit(priv, FH39_TSSR_TX_STATUS, FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), @@ -1857,7 +1862,7 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv) struct iwl_host_cmd cmd = { .id = REPLY_RXON_ASSOC, .len = sizeof(rxon_assoc), - .meta.flags = CMD_WANT_SKB, + .flags = CMD_WANT_SKB, .data = &rxon_assoc, }; const struct iwl_rxon_cmd *rxon1 = &priv->staging_rxon; @@ -1881,14 +1886,14 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv) if (rc) return rc; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_RXON_ASSOC command\n"); rc = -EIO; } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return rc; } @@ -1986,7 +1991,7 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) staging_rxon->reserved4 = 0; staging_rxon->reserved5 = 0; - iwl_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto); + iwl_set_rxon_hwcrypto(priv, !iwl3945_mod_params.sw_crypto); /* Apply the new configuration */ rc = iwl_send_cmd_pdu(priv, REPLY_RXON, @@ -2551,7 +2556,7 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) } /* Assign number of Usable TX queues */ - priv->hw_params.max_txq_num = TFD_QUEUE_MAX; + priv->hw_params.max_txq_num = IWL39_NUM_QUEUES; priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd); priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K; @@ -2562,6 +2567,7 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.bcast_sta_id = IWL3945_BROADCAST_ID; priv->hw_params.rx_wrt_ptr_reg = FH39_RSCSR_CHNL0_WPTR; + priv->hw_params.max_beacon_itrvl = IWL39_MAX_UCODE_BEACON_INTERVAL; return 0; } @@ -2784,11 +2790,50 @@ static int iwl3945_load_bsm(struct iwl_priv *priv) return 0; } +#define IWL3945_UCODE_GET(item) \ +static u32 iwl3945_ucode_get_##item(const struct iwl_ucode_header *ucode,\ + u32 api_ver) \ +{ \ + return le32_to_cpu(ucode->u.v1.item); \ +} + +static u32 iwl3945_ucode_get_header_size(u32 api_ver) +{ + return UCODE_HEADER_SIZE(1); +} +static u32 iwl3945_ucode_get_build(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return 0; +} +static u8 *iwl3945_ucode_get_data(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return (u8 *) ucode->u.v1.data; +} + +IWL3945_UCODE_GET(inst_size); +IWL3945_UCODE_GET(data_size); +IWL3945_UCODE_GET(init_size); +IWL3945_UCODE_GET(init_data_size); +IWL3945_UCODE_GET(boot_size); + static struct iwl_hcmd_ops iwl3945_hcmd = { .rxon_assoc = iwl3945_send_rxon_assoc, .commit_rxon = iwl3945_commit_rxon, }; +static struct iwl_ucode_ops iwl3945_ucode = { + .get_header_size = iwl3945_ucode_get_header_size, + .get_build = iwl3945_ucode_get_build, + .get_inst_size = iwl3945_ucode_get_inst_size, + .get_data_size = iwl3945_ucode_get_data_size, + .get_init_size = iwl3945_ucode_get_init_size, + .get_init_data_size = iwl3945_ucode_get_init_data_size, + .get_boot_size = iwl3945_ucode_get_boot_size, + .get_data = iwl3945_ucode_get_data, +}; + static struct iwl_lib_ops iwl3945_lib = { .txq_attach_buf_to_tfd = iwl3945_hw_txq_attach_buf_to_tfd, .txq_free_tfd = iwl3945_hw_txq_free_tfd, @@ -2808,8 +2853,8 @@ static struct iwl_lib_ops iwl3945_lib = { EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_REGULATORY_BAND_NO_FAT, - EEPROM_REGULATORY_BAND_NO_FAT, + EEPROM_REGULATORY_BAND_NO_HT40, + EEPROM_REGULATORY_BAND_NO_HT40, }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwl3945_eeprom_acquire_semaphore, @@ -2829,6 +2874,7 @@ static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = { }; static struct iwl_ops iwl3945_ops = { + .ucode = &iwl3945_ucode, .lib = &iwl3945_lib, .hcmd = &iwl3945_hcmd, .utils = &iwl3945_hcmd_utils, @@ -2844,7 +2890,8 @@ static struct iwl_cfg iwl3945_bg_cfg = { .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .ops = &iwl3945_ops, .mod_params = &iwl3945_mod_params, - .use_isr_legacy = true + .use_isr_legacy = true, + .ht_greenfield_support = false, }; static struct iwl_cfg iwl3945_abg_cfg = { @@ -2857,7 +2904,8 @@ static struct iwl_cfg iwl3945_abg_cfg = { .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .ops = &iwl3945_ops, .mod_params = &iwl3945_mod_params, - .use_isr_legacy = true + .use_isr_legacy = true, + .ht_greenfield_support = false, }; struct pci_device_id iwl3945_hw_card_ids[] = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index 2de6471d4be..f2403690991 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -111,9 +111,6 @@ enum iwl3945_antenna { #define IWL_TX_FIFO_HCCA_2 6 #define IWL_TX_FIFO_NONE 7 -/* Minimum number of queues. MAX_NUM is defined in hw specific files */ -#define IWL39_MIN_NUM_QUEUES 4 - #define IEEE80211_DATA_LEN 2304 #define IEEE80211_4ADDR_LEN 30 #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) @@ -257,10 +254,11 @@ extern int iwl3945_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq); extern unsigned int iwl3945_hw_get_beacon_cmd(struct iwl_priv *priv, struct iwl3945_frame *frame, u8 rate); -void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, struct iwl_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, - int sta_id, int tx_id); +void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, + int sta_id, int tx_id); extern int iwl3945_hw_reg_send_txpower(struct iwl_priv *priv); extern int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power); extern void iwl3945_hw_rx_statistics(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h index a71a489096f..b34322a3245 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h @@ -188,7 +188,7 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr) * * 1) Regulatory information (max txpower and channel usage flags) is provided * separately for each channel that can possibly supported by 4965. - * 40 MHz wide (.11n fat) channels are listed separately from 20 MHz + * 40 MHz wide (.11n HT40) channels are listed separately from 20 MHz * (legacy) channels. * * See struct iwl4965_eeprom_channel for format, and struct iwl4965_eeprom @@ -251,8 +251,8 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr) * no reduction (such as with regulatory txpower limits) is required. * * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel - * widths and 40 Mhz (.11n fat) channel widths; there is no separate - * factory measurement for fat channels. + * widths and 40 Mhz (.11n HT40) channel widths; there is no separate + * factory measurement for ht40 channels. * * The result of this step is the final target txpower. The rest of * the steps figure out the proper settings for the device to achieve diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 8f3d4bc6a03..6a13bfbc9d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -46,7 +46,7 @@ #include "iwl-sta.h" static int iwl4965_send_tx_power(struct iwl_priv *priv); -static int iwl4965_hw_get_temperature(const struct iwl_priv *priv); +static int iwl4965_hw_get_temperature(struct iwl_priv *priv); /* Highest firmware API version supported */ #define IWL4965_UCODE_API_MAX 2 @@ -293,7 +293,7 @@ restart: queue_work(priv->workqueue, &priv->restart); } -static bool is_fat_channel(__le32 rxon_flags) +static bool is_ht40_channel(__le32 rxon_flags) { int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; @@ -728,7 +728,7 @@ static int iwl4965_alive_notify(struct iwl_priv *priv) static struct iwl_sensitivity_ranges iwl4965_sensitivity = { .min_nrg_cck = 97, - .max_nrg_cck = 0, + .max_nrg_cck = 0, /* not used, set to 0 */ .auto_corr_min_ofdm = 85, .auto_corr_min_ofdm_mrc = 170, @@ -752,7 +752,8 @@ static struct iwl_sensitivity_ranges iwl4965_sensitivity = { static void iwl4965_set_ct_threshold(struct iwl_priv *priv) { /* want Kelvin */ - priv->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD); + priv->hw_params.ct_kill_threshold = + CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY); } /** @@ -781,7 +782,7 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.max_data_size = IWL49_RTC_DATA_SIZE; priv->hw_params.max_inst_size = IWL49_RTC_INST_SIZE; priv->hw_params.max_bsm_size = BSM_SRAM_SIZE; - priv->hw_params.fat_channel = BIT(IEEE80211_BAND_5GHZ); + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ); priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; @@ -1241,7 +1242,7 @@ static const struct gain_entry gain_table[2][108] = { }; static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, - u8 is_fat, u8 ctrl_chan_high, + u8 is_ht40, u8 ctrl_chan_high, struct iwl4965_tx_power_db *tx_power_tbl) { u8 saturation_power; @@ -1273,8 +1274,8 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, user_target_power = 2 * priv->tx_power_user_lmt; /* Get current (RXON) channel, band, width */ - IWL_DEBUG_TXPOWER(priv, "chan %d band %d is_fat %d\n", channel, band, - is_fat); + IWL_DEBUG_TXPOWER(priv, "chan %d band %d is_ht40 %d\n", channel, band, + is_ht40); ch_info = iwl_get_channel_info(priv, priv->band, channel); @@ -1293,7 +1294,7 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, IWL_DEBUG_TXPOWER(priv, "channel %d belongs to txatten group %d\n", channel, txatten_grp); - if (is_fat) { + if (is_ht40) { if (ctrl_chan_high) channel -= 2; else @@ -1317,8 +1318,8 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, /* regulatory txpower limits ... reg_limit values are in half-dBm, * max_power_avg values are in dBm, convert * 2 */ - if (is_fat) - reg_limit = ch_info->fat_max_power_avg * 2; + if (is_ht40) + reg_limit = ch_info->ht40_max_power_avg * 2; else reg_limit = ch_info->max_power_avg * 2; @@ -1484,7 +1485,7 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, /** * iwl4965_send_tx_power - Configure the TXPOWER level user limit * - * Uses the active RXON for channel, band, and characteristics (fat, high) + * Uses the active RXON for channel, band, and characteristics (ht40, high) * The power limit is taken from priv->tx_power_user_lmt. */ static int iwl4965_send_tx_power(struct iwl_priv *priv) @@ -1492,7 +1493,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) struct iwl4965_txpowertable_cmd cmd = { 0 }; int ret; u8 band = 0; - bool is_fat = false; + bool is_ht40 = false; u8 ctrl_chan_high = 0; if (test_bit(STATUS_SCANNING, &priv->status)) { @@ -1505,9 +1506,9 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) band = priv->band == IEEE80211_BAND_2GHZ; - is_fat = is_fat_channel(priv->active_rxon.flags); + is_ht40 = is_ht40_channel(priv->active_rxon.flags); - if (is_fat && + if (is_ht40 && (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; @@ -1516,7 +1517,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) ret = iwl4965_fill_txpower_tbl(priv, band, le16_to_cpu(priv->active_rxon.channel), - is_fat, ctrl_chan_high, &cmd.tx_power); + is_ht40, ctrl_chan_high, &cmd.tx_power); if (ret) goto out; @@ -1570,7 +1571,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) { int rc; u8 band = 0; - bool is_fat = false; + bool is_ht40 = false; u8 ctrl_chan_high = 0; struct iwl4965_channel_switch_cmd cmd = { 0 }; const struct iwl_channel_info *ch_info; @@ -1579,9 +1580,9 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) ch_info = iwl_get_channel_info(priv, priv->band, channel); - is_fat = is_fat_channel(priv->staging_rxon.flags); + is_ht40 = is_ht40_channel(priv->staging_rxon.flags); - if (is_fat && + if (is_ht40 && (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; @@ -1596,7 +1597,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) else cmd.expect_beacon = 1; - rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_fat, + rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_ht40, ctrl_chan_high, &cmd.tx_power); if (rc) { IWL_DEBUG_11H(priv, "error:%d fill txpower_tbl\n", rc); @@ -1655,7 +1656,7 @@ static s32 sign_extend(u32 oper, int index) * * A return of <0 indicates bogus data in the statistics */ -static int iwl4965_hw_get_temperature(const struct iwl_priv *priv) +static int iwl4965_hw_get_temperature(struct iwl_priv *priv) { s32 temperature; s32 vt; @@ -1663,8 +1664,8 @@ static int iwl4965_hw_get_temperature(const struct iwl_priv *priv) u32 R4; if (test_bit(STATUS_TEMPERATURE, &priv->status) && - (priv->statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)) { - IWL_DEBUG_TEMP(priv, "Running FAT temperature calibration\n"); + (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)) { + IWL_DEBUG_TEMP(priv, "Running HT40 temperature calibration\n"); R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]); R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[1]); R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[1]); @@ -1772,6 +1773,7 @@ static void iwl4965_temperature_calib(struct iwl_priv *priv) } priv->temperature = temp; + iwl_tt_handler(priv); set_bit(STATUS_TEMPERATURE, &priv->status); if (!priv->disable_tx_power_cal && @@ -2221,12 +2223,50 @@ static void iwl4965_cancel_deferred_work(struct iwl_priv *priv) cancel_work_sync(&priv->txpower_work); } +#define IWL4965_UCODE_GET(item) \ +static u32 iwl4965_ucode_get_##item(const struct iwl_ucode_header *ucode,\ + u32 api_ver) \ +{ \ + return le32_to_cpu(ucode->u.v1.item); \ +} + +static u32 iwl4965_ucode_get_header_size(u32 api_ver) +{ + return UCODE_HEADER_SIZE(1); +} +static u32 iwl4965_ucode_get_build(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return 0; +} +static u8 *iwl4965_ucode_get_data(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return (u8 *) ucode->u.v1.data; +} + +IWL4965_UCODE_GET(inst_size); +IWL4965_UCODE_GET(data_size); +IWL4965_UCODE_GET(init_size); +IWL4965_UCODE_GET(init_data_size); +IWL4965_UCODE_GET(boot_size); + static struct iwl_hcmd_ops iwl4965_hcmd = { .rxon_assoc = iwl4965_send_rxon_assoc, .commit_rxon = iwl_commit_rxon, .set_rxon_chain = iwl_set_rxon_chain, }; +static struct iwl_ucode_ops iwl4965_ucode = { + .get_header_size = iwl4965_ucode_get_header_size, + .get_build = iwl4965_ucode_get_build, + .get_inst_size = iwl4965_ucode_get_inst_size, + .get_data_size = iwl4965_ucode_get_data_size, + .get_init_size = iwl4965_ucode_get_init_size, + .get_init_data_size = iwl4965_ucode_get_init_data_size, + .get_boot_size = iwl4965_ucode_get_boot_size, + .get_data = iwl4965_ucode_get_data, +}; static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = { .get_hcmd_size = iwl4965_get_hcmd_size, .build_addsta_hcmd = iwl4965_build_addsta_hcmd, @@ -2266,8 +2306,8 @@ static struct iwl_lib_ops iwl4965_lib = { EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_4965_REGULATORY_BAND_24_FAT_CHANNELS, - EEPROM_4965_REGULATORY_BAND_52_FAT_CHANNELS + EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, + EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, @@ -2287,6 +2327,7 @@ static struct iwl_lib_ops iwl4965_lib = { }; static struct iwl_ops iwl4965_ops = { + .ucode = &iwl4965_ucode, .lib = &iwl4965_lib, .hcmd = &iwl4965_hcmd, .utils = &iwl4965_hcmd_utils, @@ -2303,7 +2344,8 @@ struct iwl_cfg iwl4965_agn_cfg = { .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, .ops = &iwl4965_ops, .mod_params = &iwl4965_mod_params, - .use_isr_legacy = true + .use_isr_legacy = true, + .ht_greenfield_support = false, }; /* Module firmware */ @@ -2313,8 +2355,6 @@ module_param_named(antenna, iwl4965_mod_params.antenna, int, 0444); MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); -module_param_named(debug, iwl4965_mod_params.debug, uint, 0444); -MODULE_PARM_DESC(debug, "debug output mask"); module_param_named( disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, 0444); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index b3c648ce8c7..1d539e3b8db 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -91,7 +91,7 @@ static int iwl5000_apm_stop_master(struct iwl_priv *priv) } -static int iwl5000_apm_init(struct iwl_priv *priv) +int iwl5000_apm_init(struct iwl_priv *priv) { int ret = 0; @@ -137,7 +137,7 @@ static int iwl5000_apm_init(struct iwl_priv *priv) } /* FIXME: this is identical to 4965 */ -static void iwl5000_apm_stop(struct iwl_priv *priv) +void iwl5000_apm_stop(struct iwl_priv *priv) { unsigned long flags; @@ -156,7 +156,7 @@ static void iwl5000_apm_stop(struct iwl_priv *priv) } -static int iwl5000_apm_reset(struct iwl_priv *priv) +int iwl5000_apm_reset(struct iwl_priv *priv) { int ret = 0; @@ -198,7 +198,8 @@ out: } -static void iwl5000_nic_config(struct iwl_priv *priv) +/* NIC configuration for 5000 series and up */ +void iwl5000_nic_config(struct iwl_priv *priv) { unsigned long flags; u16 radio_cfg; @@ -239,11 +240,11 @@ static void iwl5000_nic_config(struct iwl_priv *priv) APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + spin_unlock_irqrestore(&priv->lock, flags); } - /* * EEPROM */ @@ -283,7 +284,7 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address) return (address & ADDRESS_MSK) + (offset << 1); } -static u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv) +u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv) { struct iwl_eeprom_calib_hdr { u8 version; @@ -388,7 +389,7 @@ void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info, static struct iwl_sensitivity_ranges iwl5000_sensitivity = { .min_nrg_cck = 95, - .max_nrg_cck = 0, + .max_nrg_cck = 0, /* not used, set to 0 */ .auto_corr_min_ofdm = 90, .auto_corr_min_ofdm_mrc = 170, .auto_corr_min_ofdm_x1 = 120, @@ -407,7 +408,29 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = { .nrg_th_ofdm = 95, }; -static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, +static struct iwl_sensitivity_ranges iwl5150_sensitivity = { + .min_nrg_cck = 95, + .max_nrg_cck = 0, /* not used, set to 0 */ + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + /* max = min for performance bug in 5150 DSP */ + .auto_corr_max_ofdm_x1 = 105, + .auto_corr_max_ofdm_mrc_x1 = 220, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, +}; + +const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) { u32 address = eeprom_indirect_address(priv, offset); @@ -418,7 +441,7 @@ static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, static void iwl5150_set_ct_threshold(struct iwl_priv *priv) { const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; - s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) - + s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - iwl_temp_calib_to_offset(priv); priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; @@ -427,7 +450,7 @@ static void iwl5150_set_ct_threshold(struct iwl_priv *priv) static void iwl5000_set_ct_threshold(struct iwl_priv *priv) { /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; } /* @@ -471,7 +494,7 @@ static void iwl5000_rx_calib_result(struct iwl_priv *priv, { struct iwl_rx_packet *pkt = (void *)rxb->skb->data; struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw; - int len = le32_to_cpu(pkt->len) & FH_RSCSR_FRAME_SIZE_MSK; + int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; int index; /* reduce the size of the length field itself */ @@ -602,7 +625,7 @@ static int iwl5000_load_given_ucode(struct iwl_priv *priv, return ret; } -static int iwl5000_load_ucode(struct iwl_priv *priv) +int iwl5000_load_ucode(struct iwl_priv *priv) { int ret = 0; @@ -629,7 +652,7 @@ static int iwl5000_load_ucode(struct iwl_priv *priv) return ret; } -static void iwl5000_init_alive_start(struct iwl_priv *priv) +void iwl5000_init_alive_start(struct iwl_priv *priv) { int ret = 0; @@ -705,7 +728,7 @@ static int iwl5000_send_wimax_coex(struct iwl_priv *priv) sizeof(coex_cmd), &coex_cmd); } -static int iwl5000_alive_notify(struct iwl_priv *priv) +int iwl5000_alive_notify(struct iwl_priv *priv) { u32 a; unsigned long flags; @@ -792,7 +815,7 @@ static int iwl5000_alive_notify(struct iwl_priv *priv) return 0; } -static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) +int iwl5000_hw_set_hw_params(struct iwl_priv *priv) { if ((priv->cfg->mod_params->num_of_queues > IWL50_NUM_QUEUES) || (priv->cfg->mod_params->num_of_queues < IWL_MIN_NUM_QUEUES)) { @@ -822,12 +845,10 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) } priv->hw_params.max_bsm_size = 0; - priv->hw_params.fat_channel = BIT(IEEE80211_BAND_2GHZ) | + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; - priv->hw_params.sens = &iwl5000_sensitivity; - priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; @@ -836,9 +857,11 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) if (priv->cfg->ops->lib->temp_ops.set_ct_kill) priv->cfg->ops->lib->temp_ops.set_ct_kill(priv); + /* Set initial sensitivity parameters */ /* Set initial calibration set */ switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { case CSR_HW_REV_TYPE_5150: + priv->hw_params.sens = &iwl5150_sensitivity; priv->hw_params.calib_init_cfg = BIT(IWL_CALIB_DC) | BIT(IWL_CALIB_LO) | @@ -847,6 +870,7 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) break; default: + priv->hw_params.sens = &iwl5000_sensitivity; priv->hw_params.calib_init_cfg = BIT(IWL_CALIB_XTAL) | BIT(IWL_CALIB_LO) | @@ -862,7 +886,7 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) /** * iwl5000_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ -static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, +void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, struct iwl_tx_queue *txq, u16 byte_cnt) { @@ -902,7 +926,7 @@ static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } -static void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, +void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, struct iwl_tx_queue *txq) { struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr; @@ -957,7 +981,7 @@ static void iwl5000_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id) (1 << IWL50_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } -static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, +int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, int tx_fifo, int sta_id, int tid, u16 ssn_idx) { unsigned long flags; @@ -1018,7 +1042,7 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, return 0; } -static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, +int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, u16 ssn_idx, u8 tx_fifo) { if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) || @@ -1061,7 +1085,7 @@ u16 iwl5000_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask * must be called under priv->lock and mac access */ -static void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask) +void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask) { iwl_write_prph(priv, IWL50_SCD_TXFACT, mask); } @@ -1282,13 +1306,13 @@ u16 iwl5000_get_hcmd_size(u8 cmd_id, u16 len) return len; } -static void iwl5000_setup_deferred_work(struct iwl_priv *priv) +void iwl5000_setup_deferred_work(struct iwl_priv *priv) { /* in 5000 the tx power calibration is done in uCode */ priv->disable_tx_power_cal = 1; } -static void iwl5000_rx_handler_setup(struct iwl_priv *priv) +void iwl5000_rx_handler_setup(struct iwl_priv *priv) { /* init calibration handlers */ priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] = @@ -1299,7 +1323,7 @@ static void iwl5000_rx_handler_setup(struct iwl_priv *priv) } -static int iwl5000_hw_valid_rtc_data_addr(u32 addr) +int iwl5000_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IWL50_RTC_DATA_LOWER_BOUND) && (addr < IWL50_RTC_DATA_UPPER_BOUND); @@ -1351,7 +1375,7 @@ static int iwl5000_send_rxon_assoc(struct iwl_priv *priv) return ret; } -static int iwl5000_send_tx_power(struct iwl_priv *priv) +int iwl5000_send_tx_power(struct iwl_priv *priv) { struct iwl5000_tx_power_dbm_cmd tx_power_cmd; u8 tx_ant_cfg_cmd; @@ -1371,10 +1395,11 @@ static int iwl5000_send_tx_power(struct iwl_priv *priv) NULL); } -static void iwl5000_temperature(struct iwl_priv *priv) +void iwl5000_temperature(struct iwl_priv *priv) { /* store temperature from statistics (in Celsius) */ priv->temperature = le32_to_cpu(priv->statistics.general.temperature); + iwl_tt_handler(priv); } static void iwl5150_temperature(struct iwl_priv *priv) @@ -1386,6 +1411,7 @@ static void iwl5150_temperature(struct iwl_priv *priv) vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; /* now vt hold the temperature in Kelvin */ priv->temperature = KELVIN_TO_CELSIUS(vt); + iwl_tt_handler(priv); } /* Calc max signal level (dBm) among 3 possible receivers */ @@ -1426,6 +1452,44 @@ int iwl5000_calc_rssi(struct iwl_priv *priv, return max_rssi - agc - IWL49_RSSI_OFFSET; } +#define IWL5000_UCODE_GET(item) \ +static u32 iwl5000_ucode_get_##item(const struct iwl_ucode_header *ucode,\ + u32 api_ver) \ +{ \ + if (api_ver <= 2) \ + return le32_to_cpu(ucode->u.v1.item); \ + return le32_to_cpu(ucode->u.v2.item); \ +} + +static u32 iwl5000_ucode_get_header_size(u32 api_ver) +{ + if (api_ver <= 2) + return UCODE_HEADER_SIZE(1); + return UCODE_HEADER_SIZE(2); +} + +static u32 iwl5000_ucode_get_build(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + if (api_ver <= 2) + return 0; + return le32_to_cpu(ucode->u.v2.build); +} + +static u8 *iwl5000_ucode_get_data(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + if (api_ver <= 2) + return (u8 *) ucode->u.v1.data; + return (u8 *) ucode->u.v2.data; +} + +IWL5000_UCODE_GET(inst_size); +IWL5000_UCODE_GET(data_size); +IWL5000_UCODE_GET(init_size); +IWL5000_UCODE_GET(init_data_size); +IWL5000_UCODE_GET(boot_size); + struct iwl_hcmd_ops iwl5000_hcmd = { .rxon_assoc = iwl5000_send_rxon_assoc, .commit_rxon = iwl_commit_rxon, @@ -1441,6 +1505,17 @@ struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = { .calc_rssi = iwl5000_calc_rssi, }; +struct iwl_ucode_ops iwl5000_ucode = { + .get_header_size = iwl5000_ucode_get_header_size, + .get_build = iwl5000_ucode_get_build, + .get_inst_size = iwl5000_ucode_get_inst_size, + .get_data_size = iwl5000_ucode_get_data_size, + .get_init_size = iwl5000_ucode_get_init_size, + .get_init_data_size = iwl5000_ucode_get_init_data_size, + .get_boot_size = iwl5000_ucode_get_boot_size, + .get_data = iwl5000_ucode_get_data, +}; + struct iwl_lib_ops iwl5000_lib = { .set_hw_params = iwl5000_hw_set_hw_params, .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, @@ -1473,8 +1548,8 @@ struct iwl_lib_ops iwl5000_lib = { EEPROM_5000_REG_BAND_3_CHANNELS, EEPROM_5000_REG_BAND_4_CHANNELS, EEPROM_5000_REG_BAND_5_CHANNELS, - EEPROM_5000_REG_BAND_24_FAT_CHANNELS, - EEPROM_5000_REG_BAND_52_FAT_CHANNELS + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, @@ -1523,8 +1598,8 @@ static struct iwl_lib_ops iwl5150_lib = { EEPROM_5000_REG_BAND_3_CHANNELS, EEPROM_5000_REG_BAND_4_CHANNELS, EEPROM_5000_REG_BAND_5_CHANNELS, - EEPROM_5000_REG_BAND_24_FAT_CHANNELS, - EEPROM_5000_REG_BAND_52_FAT_CHANNELS + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, @@ -1542,12 +1617,14 @@ static struct iwl_lib_ops iwl5150_lib = { }; struct iwl_ops iwl5000_ops = { + .ucode = &iwl5000_ucode, .lib = &iwl5000_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, }; static struct iwl_ops iwl5150_ops = { + .ucode = &iwl5000_ucode, .lib = &iwl5150_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, @@ -1576,6 +1653,7 @@ struct iwl_cfg iwl5300_agn_cfg = { .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5100_bg_cfg = { @@ -1592,6 +1670,7 @@ struct iwl_cfg iwl5100_bg_cfg = { .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5100_abg_cfg = { @@ -1608,6 +1687,7 @@ struct iwl_cfg iwl5100_abg_cfg = { .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5100_agn_cfg = { @@ -1624,6 +1704,7 @@ struct iwl_cfg iwl5100_agn_cfg = { .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5350_agn_cfg = { @@ -1640,6 +1721,7 @@ struct iwl_cfg iwl5350_agn_cfg = { .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5150_agn_cfg = { @@ -1656,6 +1738,7 @@ struct iwl_cfg iwl5150_agn_cfg = { .valid_tx_ant = ANT_A, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); @@ -1664,8 +1747,6 @@ MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX)); module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, 0444); MODULE_PARM_DESC(swcrypto50, "using software crypto engine (default 0 [hardware])\n"); -module_param_named(debug50, iwl50_mod_params.debug, uint, 0444); -MODULE_PARM_DESC(debug50, "50XX debug output mask"); module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, 0444); MODULE_PARM_DESC(queues_num50, "number of hw queues in 50xx series"); module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, 0444); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index bd438d8acf5..82b9c93dff5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -46,8 +46,8 @@ #include "iwl-5000-hw.h" /* Highest firmware API version supported */ -#define IWL6000_UCODE_API_MAX 2 -#define IWL6050_UCODE_API_MAX 2 +#define IWL6000_UCODE_API_MAX 4 +#define IWL6050_UCODE_API_MAX 4 /* Lowest firmware API version supported */ #define IWL6000_UCODE_API_MIN 1 @@ -61,6 +61,82 @@ #define _IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE #api ".ucode" #define IWL6050_MODULE_FIRMWARE(api) _IWL6050_MODULE_FIRMWARE(api) +static void iwl6000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 6000 series */ +static void iwl6000_nic_config(struct iwl_priv *priv) +{ + iwl5000_nic_config(priv); + + /* no locking required for register write */ + if (priv->cfg->pa_type == IWL_PA_HYBRID) { + /* 2x2 hybrid phy type */ + iwl_write32(priv, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB); + } else if (priv->cfg->pa_type == IWL_PA_INTERNAL) { + /* 2x2 IPA phy type */ + iwl_write32(priv, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); + } + /* else do nothing, uCode configured */ +} + +static struct iwl_lib_ops iwl6000_lib = { + .set_hw_params = iwl5000_hw_set_hw_params, + .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, + .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, + .txq_set_sched = iwl5000_txq_set_sched, + .txq_agg_enable = iwl5000_txq_agg_enable, + .txq_agg_disable = iwl5000_txq_agg_disable, + .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = iwl_hw_txq_free_tfd, + .txq_init = iwl_hw_tx_queue_init, + .rx_handler_setup = iwl5000_rx_handler_setup, + .setup_deferred_work = iwl5000_setup_deferred_work, + .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .load_ucode = iwl5000_load_ucode, + .init_alive_start = iwl5000_init_alive_start, + .alive_notify = iwl5000_alive_notify, + .send_tx_power = iwl5000_send_tx_power, + .update_chain_flags = iwl_update_chain_flags, + .apm_ops = { + .init = iwl5000_apm_init, + .reset = iwl5000_apm_reset, + .stop = iwl5000_apm_stop, + .config = iwl6000_nic_config, + .set_pwr_src = iwl_set_pwr_src, + }, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_5000_REG_BAND_1_CHANNELS, + EEPROM_5000_REG_BAND_2_CHANNELS, + EEPROM_5000_REG_BAND_3_CHANNELS, + EEPROM_5000_REG_BAND_4_CHANNELS, + EEPROM_5000_REG_BAND_5_CHANNELS, + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS + }, + .verify_signature = iwlcore_eeprom_verify_signature, + .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, + .release_semaphore = iwlcore_eeprom_release_semaphore, + .calib_version = iwl5000_eeprom_calib_version, + .query_addr = iwl5000_eeprom_query_addr, + .update_enhanced_txpower = iwlcore_eeprom_enhanced_txpower, + }, + .post_associate = iwl_post_associate, + .isr = iwl_isr_ict, + .config_ap = iwl_config_ap, + .temp_ops = { + .temperature = iwl5000_temperature, + .set_ct_kill = iwl6000_set_ct_threshold, + }, +}; + static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = { .get_hcmd_size = iwl5000_get_hcmd_size, .build_addsta_hcmd = iwl5000_build_addsta_hcmd, @@ -69,41 +145,57 @@ static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = { }; static struct iwl_ops iwl6000_ops = { - .lib = &iwl5000_lib, + .ucode = &iwl5000_ucode, + .lib = &iwl6000_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl6000_hcmd_utils, }; -struct iwl_cfg iwl6000_2ag_cfg = { - .name = "6000 Series 2x2 AG", + +/* + * "h": Hybrid configuration, use both internal and external Power Amplifier + */ +struct iwl_cfg iwl6000h_2agn_cfg = { + .name = "6000 Series 2x2 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, - .sku = IWL_SKU_A|IWL_SKU_G, + .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, - .valid_tx_ant = ANT_BC, - .valid_rx_ant = ANT_BC, + .valid_tx_ant = ANT_AB, + .valid_rx_ant = ANT_AB, .need_pll_cfg = false, + .pa_type = IWL_PA_HYBRID, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; -struct iwl_cfg iwl6000_2agn_cfg = { +/* + * "i": Internal configuration, use internal Power Amplifier + */ +struct iwl_cfg iwl6000i_2agn_cfg = { .name = "6000 Series 2x2 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, - .valid_tx_ant = ANT_AB, - .valid_rx_ant = ANT_AB, + .valid_tx_ant = ANT_BC, + .valid_rx_ant = ANT_BC, .need_pll_cfg = false, + .pa_type = IWL_PA_INTERNAL, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl6050_2agn_cfg = { @@ -113,13 +205,17 @@ struct iwl_cfg iwl6050_2agn_cfg = { .ucode_api_min = IWL6050_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_AB, .valid_rx_ant = ANT_AB, .need_pll_cfg = false, + .pa_type = IWL_PA_SYSTEM, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl6000_3agn_cfg = { @@ -129,13 +225,17 @@ struct iwl_cfg iwl6000_3agn_cfg = { .ucode_api_min = IWL6000_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = false, + .pa_type = IWL_PA_SYSTEM, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl6050_3agn_cfg = { @@ -145,13 +245,17 @@ struct iwl_cfg iwl6050_3agn_cfg = { .ucode_api_min = IWL6050_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = false, + .pa_type = IWL_PA_SYSTEM, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index ff20e5048a5..26ec969e265 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -97,7 +97,7 @@ struct iwl_scale_tbl_info { enum iwl_table_type lq_type; u8 ant_type; u8 is_SGI; /* 1 = short guard interval */ - u8 is_fat; /* 1 = 40 MHz channel width */ + u8 is_ht40; /* 1 = 40 MHz channel width */ u8 is_dup; /* 1 = duplicated data streams */ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ u8 max_search; /* maximun number of tables we can search */ @@ -177,7 +177,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, struct sk_buff *skb, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta); -static void rs_fill_link_cmd(const struct iwl_priv *priv, +static void rs_fill_link_cmd(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, u32 rate_n_flags); @@ -332,6 +332,9 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, } else return MAX_TID_COUNT; + if (unlikely(tid >= TID_MAX_LOAD_COUNT)) + return MAX_TID_COUNT; + tl = &lq_data->load[tid]; curr_time -= curr_time % TID_ROUND_VALUE; @@ -539,11 +542,11 @@ static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, RATE_MCS_ANT_ABC_MSK); if (is_Ht(tbl->lq_type)) { - if (tbl->is_fat) { + if (tbl->is_ht40) { if (tbl->is_dup) rate_n_flags |= RATE_MCS_DUP_MSK; else - rate_n_flags |= RATE_MCS_FAT_MSK; + rate_n_flags |= RATE_MCS_HT40_MSK; } if (tbl->is_SGI) rate_n_flags |= RATE_MCS_SGI_MSK; @@ -579,7 +582,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, return -EINVAL; } tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_fat = 0; + tbl->is_ht40 = 0; tbl->is_dup = 0; tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); tbl->lq_type = LQ_NONE; @@ -598,9 +601,9 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, if (rate_n_flags & RATE_MCS_SGI_MSK) tbl->is_SGI = 1; - if ((rate_n_flags & RATE_MCS_FAT_MSK) || + if ((rate_n_flags & RATE_MCS_HT40_MSK) || (rate_n_flags & RATE_MCS_DUP_MSK)) - tbl->is_fat = 1; + tbl->is_ht40 = 1; if (rate_n_flags & RATE_MCS_DUP_MSK) tbl->is_dup = 1; @@ -654,19 +657,15 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, return 1; } -/* in 4965 we don't use greenfield at all */ -static inline u8 rs_use_green(struct iwl_priv *priv, - struct ieee80211_conf *conf) +/** + * Green-field mode is valid if the station supports it and + * there are no non-GF stations present in the BSS. + */ +static inline u8 rs_use_green(struct ieee80211_sta *sta, + struct iwl_ht_info *ht_conf) { - u8 is_green; - - if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) - is_green = 0; - else - is_green = (conf_is_ht(conf) && - priv->current_ht_config.is_green_field && - !priv->current_ht_config.non_GF_STA_present); - return is_green; + return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_conf->non_GF_STA_present); } /** @@ -776,7 +775,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, if (num_of_ant(tbl->ant_type) > 1) tbl->ant_type = ANT_A;/*FIXME:RS*/ - tbl->is_fat = 0; + tbl->is_ht40 = 0; tbl->is_SGI = 0; tbl->max_search = IWL_MAX_SEARCH; } @@ -880,7 +879,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, if ((info->status.rates[0].idx < 0) || (tbl_type.is_SGI != !!(info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.is_fat != !!(info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || + (tbl_type.is_ht40 != !!(info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || (tbl_type.is_dup != !!(info->status.rates[0].flags & IEEE80211_TX_RC_DUP_DATA)) || (tbl_type.ant_type != info->antenna_sel_tx) || (!!(tx_rate & RATE_MCS_HT_MSK) != !!(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) || @@ -1049,7 +1048,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, else tbl->expected_tpt = expected_tpt_A; } else if (is_siso(tbl->lq_type)) { - if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_ht40 && !lq_sta->is_dup) if (tbl->is_SGI) tbl->expected_tpt = expected_tpt_siso40MHzSGI; else @@ -1059,7 +1058,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, else tbl->expected_tpt = expected_tpt_siso20MHz; } else if (is_mimo2(tbl->lq_type)) { - if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_ht40 && !lq_sta->is_dup) if (tbl->is_SGI) tbl->expected_tpt = expected_tpt_mimo2_40MHzSGI; else @@ -1069,7 +1068,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, else tbl->expected_tpt = expected_tpt_mimo2_20MHz; } else if (is_mimo3(tbl->lq_type)) { - if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_ht40 && !lq_sta->is_dup) if (tbl->is_SGI) tbl->expected_tpt = expected_tpt_mimo3_40MHzSGI; else @@ -1217,22 +1216,10 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; - if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) - tbl->is_fat = 1; + if (iwl_is_ht40_tx_allowed(priv, &sta->ht_cap)) + tbl->is_ht40 = 1; else - tbl->is_fat = 0; - - /* FIXME: - don't toggle SGI here - if (tbl->is_fat) { - if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - */ + tbl->is_ht40 = 0; rs_set_expected_tpt_table(lq_sta, tbl); @@ -1283,22 +1270,10 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; rate_mask = lq_sta->active_mimo3_rate; - if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) - tbl->is_fat = 1; - else - tbl->is_fat = 0; - - /* FIXME: - don't toggle SGI here - if (tbl->is_fat) { - if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) - tbl->is_SGI = 1; + if (iwl_is_ht40_tx_allowed(priv, &sta->ht_cap)) + tbl->is_ht40 = 1; else - tbl->is_SGI = 0; - */ + tbl->is_ht40 = 0; rs_set_expected_tpt_table(lq_sta, tbl); @@ -1342,22 +1317,10 @@ static int rs_switch_to_siso(struct iwl_priv *priv, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; - if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) - tbl->is_fat = 1; - else - tbl->is_fat = 0; - - /* FIXME: - don't toggle SGI here - if (tbl->is_fat) { - if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) - tbl->is_SGI = 1; + if (iwl_is_ht40_tx_allowed(priv, &sta->ht_cap)) + tbl->is_ht40 = 1; else - tbl->is_SGI = 0; - */ + tbl->is_ht40 = 0; if (is_green) tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ @@ -1398,6 +1361,12 @@ static int rs_move_legacy_other(struct iwl_priv *priv, int ret = 0; u8 update_search_tbl_counter = 0; + if (!iwl_ht_enabled(priv)) + /* stay in Legacy */ + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_LEGACY_SWITCH_SISO) + tbl->action = IWL_LEGACY_SWITCH_SISO; for (; ;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1521,6 +1490,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; @@ -1529,6 +1499,11 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, u8 update_search_tbl_counter = 0; int ret; + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_SISO_SWITCH_ANTENNA2) { + /* stay in SISO */ + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + } for (;;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1575,13 +1550,11 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, goto out; break; case IWL_SISO_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); @@ -1655,6 +1628,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; @@ -1663,6 +1637,12 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, u8 update_search_tbl_counter = 0; int ret; + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO2_SWITCH_SISO_A || + tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + } for (;;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1709,13 +1689,11 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, break; case IWL_MIMO2_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); @@ -1791,6 +1769,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; @@ -1799,6 +1778,12 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, int ret; u8 update_search_tbl_counter = 0; + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO3_SWITCH_SISO_A || + tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + } for (;;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1867,13 +1852,11 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, break; case IWL_MIMO3_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); @@ -2003,6 +1986,25 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta) } /* + * setup rate table in uCode + * return rate_n_flags as used in the table + */ +static u32 rs_update_rate_tbl(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int index, u8 is_green) +{ + u32 rate; + + /* Update uCode's rate table. */ + rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); + rs_fill_link_cmd(priv, lq_sta, rate); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + + return rate; +} + +/* * Do rate scaling and search for new modulation mode. */ static void rs_rate_scale_perform(struct iwl_priv *priv, @@ -2066,7 +2068,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, if (is_legacy(tbl->lq_type)) lq_sta->is_green = 0; else - lq_sta->is_green = rs_use_green(priv, conf); + lq_sta->is_green = rs_use_green(sta, &priv->current_ht_config); is_green = lq_sta->is_green; /* current tx rate */ @@ -2098,6 +2100,16 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, if (!((1 << index) & rate_scale_index_msk)) { IWL_ERR(priv, "Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid*/ + tbl->lq_type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + /* get "active" rate info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + rate = rs_update_rate_tbl(priv, lq_sta, + tbl, index, is_green); + } return; } @@ -2149,8 +2161,8 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, tbl->expected_tpt[index] + 64) / 128)); /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl) { - + if (lq_sta->search_better_tbl && + (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { /* If good success, continue using the "search" mode; * no need to send new link quality command, since we're * continuing to use the setup that we've been trying. */ @@ -2278,7 +2290,11 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, ((sr > IWL_RATE_HIGH_TH) || (current_tpt > (100 * tbl->expected_tpt[low])))) scale_action = 0; - + if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) + scale_action = -1; + if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) + scale_action = -1; switch (scale_action) { case -1: /* Decrease starting rate, update uCode's rate table */ @@ -2308,15 +2324,15 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, lq_update: /* Replace uCode's rate table for the destination station. */ - if (update_lq) { - rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); - rs_fill_link_cmd(priv, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); - } - - /* Should we stay with this modulation mode, or search for a new one? */ - rs_stay_in_table(lq_sta); + if (update_lq) + rate = rs_update_rate_tbl(priv, lq_sta, + tbl, index, is_green); + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta); + } /* * Search for new modulation mode if we're: * 1) Not changing rates right now @@ -2373,7 +2389,8 @@ lq_update: * have been tried and compared, stay in this best modulation * mode for a while before next round of mode comparisons. */ if (lq_sta->enable_counter && - (lq_sta->action_counter >= tbl1->max_search)) { + (lq_sta->action_counter >= tbl1->max_search) && + iwl_ht_enabled(priv)) { if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != MAX_TID_COUNT)) { @@ -2409,7 +2426,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, int rate_idx; int i; u32 rate; - u8 use_green = rs_use_green(priv, conf); + u8 use_green = rs_use_green(sta, &priv->current_ht_config); u8 active_tbl = 0; u8 valid_tx_ant; @@ -2462,11 +2479,11 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_supported_band *sband = txrc->sband; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = priv_sta; int rate_idx; - u64 mask_bit = 0; IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); @@ -2481,22 +2498,9 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, lq_sta->max_rate_idx = -1; } - if (sta) - mask_bit = sta->supp_rates[sband->band]; - /* Send management frames and NO_ACK data using lowest rate. */ - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK || !sta || !lq_sta) { - if (!mask_bit) - info->control.rates[0].idx = - rate_lowest_index(sband, NULL); - else - info->control.rates[0].idx = - rate_lowest_index(sband, sta); - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - info->control.rates[0].count = 1; + if (rate_control_send_low(sta, priv_sta, txrc)) return; - } rate_idx = lq_sta->last_txrate_idx; @@ -2508,7 +2512,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", hdr->addr1); sta_id = iwl_add_station(priv, hdr->addr1, - false, CMD_ASYNC, NULL); + false, CMD_ASYNC, ht_cap); } if ((sta_id != IWL_INVALID_STATION)) { lq_sta->lq.sta_id = sta_id; @@ -2533,7 +2537,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; - if (lq_sta->last_rate_n_flags & RATE_MCS_FAT_MSK) + if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; @@ -2542,6 +2546,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, rate_idx = rate_lowest_index(sband, sta); else if (sband->band == IEEE80211_BAND_5GHZ) rate_idx -= IWL_FIRST_OFDM_RATE; + info->control.rates[0].flags = 0; } info->control.rates[0].idx = rate_idx; @@ -2577,6 +2582,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, int i, j; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct iwl_lq_sta *lq_sta = priv_sta; u16 mask_bit = 0; int count; @@ -2605,7 +2611,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr); sta_id = iwl_add_station(priv, sta->addr, false, - CMD_ASYNC, NULL); + CMD_ASYNC, ht_cap); } if ((sta_id != IWL_INVALID_STATION)) { lq_sta->lq.sta_id = sta_id; @@ -2618,7 +2624,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, lq_sta->is_dup = 0; lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; - lq_sta->is_green = rs_use_green(priv, conf); + lq_sta->is_green = rs_use_green(sta, &priv->current_ht_config); lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000); lq_sta->active_rate_basic = priv->active_rate_basic; lq_sta->band = priv->band; @@ -2626,19 +2632,19 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), * supp_rates[] does not; shift to convert format, force 9 MBits off. */ - lq_sta->active_siso_rate = sta->ht_cap.mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= sta->ht_cap.mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; lq_sta->active_siso_rate &= ~((u16)0x2); lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; /* Same here */ - lq_sta->active_mimo2_rate = sta->ht_cap.mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= sta->ht_cap.mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; lq_sta->active_mimo2_rate &= ~((u16)0x2); lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - lq_sta->active_mimo3_rate = sta->ht_cap.mcs.rx_mask[2] << 1; - lq_sta->active_mimo3_rate |= sta->ht_cap.mcs.rx_mask[2] & 0x1; + lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; + lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; lq_sta->active_mimo3_rate &= ~((u16)0x2); lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; @@ -2673,7 +2679,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, rs_initialize_lq(priv, conf, sta, lq_sta); } -static void rs_fill_link_cmd(const struct iwl_priv *priv, +static void rs_fill_link_cmd(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, u32 new_rate) { struct iwl_scale_tbl_info tbl_type; @@ -2920,7 +2926,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (is_siso(tbl->lq_type)) ? "SISO" : ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); desc += sprintf(buff+desc, " %s", - (tbl->is_fat) ? "40MHz" : "20MHz"); + (tbl->is_ht40) ? "40MHz" : "20MHz"); desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "", (lq_sta->is_green) ? "GF enabled" : ""); } @@ -2985,12 +2991,13 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, return -ENOMEM; for (i = 0; i < LQ_SIZE; i++) { - desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d GF=%d\n" + desc += sprintf(buff+desc, + "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", lq_sta->lq_info[i].lq_type, lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_fat, + lq_sta->lq_info[i].is_ht40, lq_sta->lq_info[i].is_dup, lq_sta->is_green, lq_sta->lq_info[i].current_rate); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 355f50ea7fe..00457bff1ed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -171,7 +171,7 @@ int iwl_commit_rxon(struct iwl_priv *priv) le16_to_cpu(priv->staging_rxon.channel), priv->staging_rxon.bssid_addr); - iwl_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto); + iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto); /* Apply the new configuration * RXON unassoc clears the station table in uCode, send it before @@ -442,8 +442,8 @@ void iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) /* Unmap tx_cmd */ if (num_tbs) pci_unmap_single(dev, - pci_unmap_addr(&txq->cmd[index]->meta, mapping), - pci_unmap_len(&txq->cmd[index]->meta, len), + pci_unmap_addr(&txq->meta[index], mapping), + pci_unmap_len(&txq->meta[index], len), PCI_DMA_BIDIRECTIONAL); /* Unmap chunks, if any. */ @@ -512,70 +512,6 @@ int iwl_hw_tx_queue_init(struct iwl_priv *priv, return 0; } - -/****************************************************************************** - * - * Misc. internal state and helper functions - * - ******************************************************************************/ - -#define MAX_UCODE_BEACON_INTERVAL 4096 - -static u16 iwl_adjust_beacon_interval(u16 beacon_val) -{ - u16 new_val = 0; - u16 beacon_factor = 0; - - beacon_factor = (beacon_val + MAX_UCODE_BEACON_INTERVAL) - / MAX_UCODE_BEACON_INTERVAL; - new_val = beacon_val / beacon_factor; - - if (!new_val) - new_val = MAX_UCODE_BEACON_INTERVAL; - - return new_val; -} - -static void iwl_setup_rxon_timing(struct iwl_priv *priv) -{ - u64 tsf; - s32 interval_tm, rem; - unsigned long flags; - struct ieee80211_conf *conf = NULL; - u16 beacon_int = 0; - - conf = ieee80211_get_hw_conf(priv->hw); - - spin_lock_irqsave(&priv->lock, flags); - priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp); - priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); - - if (priv->iw_mode == NL80211_IFTYPE_STATION) { - beacon_int = iwl_adjust_beacon_interval(priv->beacon_int); - priv->rxon_timing.atim_window = 0; - } else { - beacon_int = iwl_adjust_beacon_interval( - priv->vif->bss_conf.beacon_int); - - /* TODO: we need to get atim_window from upper stack - * for now we set to 0 */ - priv->rxon_timing.atim_window = 0; - } - - priv->rxon_timing.beacon_interval = cpu_to_le16(beacon_int); - - tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ - interval_tm = beacon_int * 1024; - rem = do_div(tsf, interval_tm); - priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem); - - spin_unlock_irqrestore(&priv->lock, flags); - IWL_DEBUG_ASSOC(priv, "beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(priv->rxon_timing.beacon_interval), - le32_to_cpu(priv->rxon_timing.beacon_init_val), - le16_to_cpu(priv->rxon_timing.atim_window)); -} - /****************************************************************************** * * Generic RX handler implementations @@ -697,7 +633,6 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = priv->status; - unsigned long reg_flags; IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", @@ -717,19 +652,12 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); iwl_write_direct32(priv, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - } - - if (flags & RF_CARD_DISABLED) { - iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - iwl_read32(priv, CSR_UCODE_DRV_GP1); - spin_lock_irqsave(&priv->reg_lock, reg_flags); - if (!iwl_grab_nic_access(priv)) - iwl_release_nic_access(priv); - spin_unlock_irqrestore(&priv->reg_lock, reg_flags); } + if (flags & RF_CARD_DISABLED) + iwl_tt_enter_ct_kill(priv); } + if (!(flags & RF_CARD_DISABLED)) + iwl_tt_exit_ct_kill(priv); if (flags & HW_CARD_DISABLED) set_bit(STATUS_RF_KILL_HW, &priv->status); @@ -964,7 +892,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { + if (iwl_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", @@ -983,7 +911,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(priv, "Microcode HW error detected. Restarting.\n"); + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(priv); @@ -999,7 +927,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1024,7 +952,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) hw_rf_kill = 1; - IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n", + IWL_WARN(priv, "RF_KILL bit toggled to %s.\n", hw_rf_kill ? "disable radio" : "enable radio"); priv->isr_stats.rfkill++; @@ -1113,7 +1041,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) iwl_enable_interrupts(priv); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { inta = iwl_read32(priv, CSR_INT); inta_mask = iwl_read32(priv, CSR_INT_MASK); inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); @@ -1144,7 +1072,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) inta = priv->inta; #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { + if (iwl_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x\n ", @@ -1156,7 +1084,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(priv, "Microcode HW error detected. Restarting.\n"); + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(priv); @@ -1172,7 +1100,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1197,7 +1125,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) hw_rf_kill = 1; - IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n", + IWL_WARN(priv, "RF_KILL bit toggled to %s.\n", hw_rf_kill ? "disable radio" : "enable radio"); priv->isr_stats.rfkill++; @@ -1348,7 +1276,7 @@ static void iwl_nic_start(struct iwl_priv *priv) */ static int iwl_read_ucode(struct iwl_priv *priv) { - struct iwl_ucode *ucode; + struct iwl_ucode_header *ucode; int ret = -EINVAL, index; const struct firmware *ucode_raw; const char *name_pre = priv->cfg->fw_name_pre; @@ -1357,7 +1285,9 @@ static int iwl_read_ucode(struct iwl_priv *priv) char buf[25]; u8 *src; size_t len; - u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size; + u32 api_ver, build; + u32 inst_size, data_size, init_size, init_data_size, boot_size; + u16 eeprom_ver; /* Ask kernel firmware_class module to get the boot firmware off disk. * request_firmware() is synchronous, file is in memory on return. */ @@ -1387,23 +1317,26 @@ static int iwl_read_ucode(struct iwl_priv *priv) if (ret < 0) goto error; - /* Make sure that we got at least our header! */ - if (ucode_raw->size < sizeof(*ucode)) { + /* Make sure that we got at least the v1 header! */ + if (ucode_raw->size < priv->cfg->ops->ucode->get_header_size(1)) { IWL_ERR(priv, "File size way too small!\n"); ret = -EINVAL; goto err_release; } /* Data from ucode file: header followed by uCode images */ - ucode = (void *)ucode_raw->data; + ucode = (struct iwl_ucode_header *)ucode_raw->data; priv->ucode_ver = le32_to_cpu(ucode->ver); api_ver = IWL_UCODE_API(priv->ucode_ver); - inst_size = le32_to_cpu(ucode->inst_size); - data_size = le32_to_cpu(ucode->data_size); - init_size = le32_to_cpu(ucode->init_size); - init_data_size = le32_to_cpu(ucode->init_data_size); - boot_size = le32_to_cpu(ucode->boot_size); + build = priv->cfg->ops->ucode->get_build(ucode, api_ver); + inst_size = priv->cfg->ops->ucode->get_inst_size(ucode, api_ver); + data_size = priv->cfg->ops->ucode->get_data_size(ucode, api_ver); + init_size = priv->cfg->ops->ucode->get_init_size(ucode, api_ver); + init_data_size = + priv->cfg->ops->ucode->get_init_data_size(ucode, api_ver); + boot_size = priv->cfg->ops->ucode->get_boot_size(ucode, api_ver); + src = priv->cfg->ops->ucode->get_data(ucode, api_ver); /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely @@ -1429,6 +1362,14 @@ static int iwl_read_ucode(struct iwl_priv *priv) IWL_UCODE_API(priv->ucode_ver), IWL_UCODE_SERIAL(priv->ucode_ver)); + if (build) + IWL_DEBUG_INFO(priv, "Build %u\n", build); + + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + IWL_DEBUG_INFO(priv, "NVM Type: %s, version: 0x%x\n", + (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + ? "OTP" : "EEPROM", eeprom_ver); + IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n", priv->ucode_ver); IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %u\n", @@ -1443,12 +1384,14 @@ static int iwl_read_ucode(struct iwl_priv *priv) boot_size); /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size < sizeof(*ucode) + + if (ucode_raw->size != + priv->cfg->ops->ucode->get_header_size(api_ver) + inst_size + data_size + init_size + init_data_size + boot_size) { - IWL_DEBUG_INFO(priv, "uCode file size %d too small\n", - (int)ucode_raw->size); + IWL_DEBUG_INFO(priv, + "uCode file size %d does not match expected size\n", + (int)ucode_raw->size); ret = -EINVAL; goto err_release; } @@ -1528,42 +1471,42 @@ static int iwl_read_ucode(struct iwl_priv *priv) /* Copy images into buffers for card's bus-master reads ... */ /* Runtime instructions (first block of data in file) */ - src = &ucode->data[0]; - len = priv->ucode_code.len; + len = inst_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %Zd\n", len); memcpy(priv->ucode_code.v_addr, src, len); + src += len; + IWL_DEBUG_INFO(priv, "uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr); /* Runtime data (2nd block) * NOTE: Copy into backup buffer will be done in iwl_up() */ - src = &ucode->data[inst_size]; - len = priv->ucode_data.len; + len = data_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode data len %Zd\n", len); memcpy(priv->ucode_data.v_addr, src, len); memcpy(priv->ucode_data_backup.v_addr, src, len); + src += len; /* Initialization instructions (3rd block) */ if (init_size) { - src = &ucode->data[inst_size + data_size]; - len = priv->ucode_init.len; + len = init_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init instr len %Zd\n", len); memcpy(priv->ucode_init.v_addr, src, len); + src += len; } /* Initialization data (4th block) */ if (init_data_size) { - src = &ucode->data[inst_size + data_size + init_size]; - len = priv->ucode_init_data.len; + len = init_data_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init data len %Zd\n", len); memcpy(priv->ucode_init_data.v_addr, src, len); + src += len; } /* Bootstrap instructions (5th block) */ - src = &ucode->data[inst_size + data_size + init_size + init_data_size]; - len = priv->ucode_boot.len; + len = boot_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %Zd\n", len); memcpy(priv->ucode_boot.v_addr, src, len); @@ -1663,7 +1606,7 @@ static void iwl_alive_start(struct iwl_priv *priv) set_bit(STATUS_READY, &priv->status); wake_up_interruptible(&priv->wait_command_queue); - iwl_power_update_mode(priv, 1); + iwl_power_update_mode(priv, true); /* reassociate for ADHOC mode */ if (priv->vif && (priv->iw_mode == NL80211_IFTYPE_ADHOC)) { @@ -1812,6 +1755,11 @@ static int iwl_prepare_card_hw(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, "iwl_prepare_card_hw enter \n"); + ret = iwl_set_hw_ready(priv); + if (priv->hw_ready) + return ret; + + /* If HW is not ready, prepare the conditions to check again */ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); @@ -1819,6 +1767,7 @@ static int iwl_prepare_card_hw(struct iwl_priv *priv) ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + /* HW should be ready by now, check again. */ if (ret != -ETIMEDOUT) iwl_set_hw_ready(priv); @@ -2126,7 +2075,7 @@ void iwl_post_associate(struct iwl_priv *priv) * If chain noise has already been run, then we need to enable * power management here */ if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) - iwl_power_update_mode(priv, 0); + iwl_power_update_mode(priv, false); /* Enable Rx differential gain and sensitivity calibrations */ iwl_chain_noise_reset(priv); @@ -2206,7 +2155,7 @@ static void iwl_mac_stop(struct ieee80211_hw *hw) priv->is_open = 0; - if (iwl_is_ready_rf(priv)) { + if (iwl_is_ready_rf(priv) || test_bit(STATUS_SCAN_HW, &priv->status)) { /* stop mac, cancel any scan request and clear * RXON_FILTER_ASSOC_MSK BIT */ @@ -2331,7 +2280,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, IWL_DEBUG_MAC80211(priv, "enter\n"); - if (priv->hw_params.sw_crypto) { + if (priv->cfg->mod_params->sw_crypto) { IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); return -EOPNOTSUPP; } @@ -2455,14 +2404,16 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw, * used for controlling the debug level. * * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. */ - static ssize_t show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - - return sprintf(buf, "0x%08X\n", priv->debug_level); + return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); } static ssize_t store_debug_level(struct device *d, struct device_attribute *attr, @@ -2475,9 +2426,12 @@ static ssize_t store_debug_level(struct device *d, ret = strict_strtoul(buf, 0, &val); if (ret) IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf); - else + else { priv->debug_level = val; - + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, + "Not enough memory to generate traffic log\n"); + } return strnlen(buf, count); } @@ -2488,39 +2442,6 @@ static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, #endif /* CONFIG_IWLWIFI_DEBUG */ -static ssize_t show_version(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - struct iwl_alive_resp *palive = &priv->card_alive; - ssize_t pos = 0; - u16 eeprom_ver; - - if (palive->is_valid) - pos += sprintf(buf + pos, - "fw version: 0x%01X.0x%01X.0x%01X.0x%01X\n" - "fw type: 0x%01X 0x%01X\n", - palive->ucode_major, palive->ucode_minor, - palive->sw_rev[0], palive->sw_rev[1], - palive->ver_type, palive->ver_subtype); - else - pos += sprintf(buf + pos, "fw not loaded\n"); - - if (priv->eeprom) { - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - pos += sprintf(buf + pos, "NVM Type: %s, version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", eeprom_ver); - - } else { - pos += sprintf(buf + pos, "EEPROM not initialzed\n"); - } - - return pos; -} - -static DEVICE_ATTR(version, S_IWUSR | S_IRUGO, show_version, NULL); - static ssize_t show_temperature(struct device *d, struct device_attribute *attr, char *buf) { @@ -2556,10 +2477,15 @@ static ssize_t store_tx_power(struct device *d, ret = strict_strtoul(buf, 10, &val); if (ret) IWL_INFO(priv, "%s is not in decimal form.\n", buf); - else - iwl_set_tx_power(priv, val, false); - - return count; + else { + ret = iwl_set_tx_power(priv, val, false); + if (ret) + IWL_ERR(priv, "failed setting tx power (0x%d).\n", + ret); + else + ret = count; + } + return ret; } static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); @@ -2644,67 +2570,6 @@ static ssize_t store_filter_flags(struct device *d, static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, store_filter_flags); -static ssize_t store_power_level(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - int ret; - unsigned long mode; - - - mutex_lock(&priv->mutex); - - ret = strict_strtoul(buf, 10, &mode); - if (ret) - goto out; - - ret = iwl_power_set_user_mode(priv, mode); - if (ret) { - IWL_DEBUG_MAC80211(priv, "failed setting power mode.\n"); - goto out; - } - ret = count; - - out: - mutex_unlock(&priv->mutex); - return ret; -} - -static ssize_t show_power_level(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - int level = priv->power_data.power_mode; - char *p = buf; - - p += sprintf(p, "%d\n", level); - return p - buf + 1; -} - -static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, - store_power_level); - -static ssize_t show_qos(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - char *p = buf; - int q; - - for (q = 0; q < AC_NUM; q++) { - p += sprintf(p, "\tcw_min\tcw_max\taifsn\ttxop\n"); - p += sprintf(p, "AC[%d]\t%u\t%u\t%u\t%u\n", q, - priv->qos_data.def_qos_parm.ac[q].cw_min, - priv->qos_data.def_qos_parm.ac[q].cw_max, - priv->qos_data.def_qos_parm.ac[q].aifsn, - priv->qos_data.def_qos_parm.ac[q].edca_txop); - } - - return p - buf + 1; -} - -static DEVICE_ATTR(qos, S_IRUGO, show_qos, NULL); static ssize_t show_statistics(struct device *d, struct device_attribute *attr, char *buf) @@ -2797,15 +2662,12 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv) static struct attribute *iwl_sysfs_entries[] = { &dev_attr_flags.attr, &dev_attr_filter_flags.attr, - &dev_attr_power_level.attr, &dev_attr_statistics.attr, &dev_attr_temperature.attr, &dev_attr_tx_power.attr, #ifdef CONFIG_IWLWIFI_DEBUG &dev_attr_debug_level.attr, #endif - &dev_attr_version.attr, - &dev_attr_qos.attr, NULL }; @@ -2849,7 +2711,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Disabling hardware scan means that mac80211 will perform scans * "the hard way", rather than using device's scan. */ if (cfg->mod_params->disable_hw_scan) { - if (cfg->mod_params->debug & IWL_DL_INFO) + if (iwl_debug_level & IWL_DL_INFO) dev_printk(KERN_DEBUG, &(pdev->dev), "Disabling hw_scan\n"); iwl_hw_ops.hw_scan = NULL; @@ -2871,9 +2733,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) priv->inta_mask = CSR_INI_SET_MASK; #ifdef CONFIG_IWLWIFI_DEBUG - priv->debug_level = priv->cfg->mod_params->debug; atomic_set(&priv->restrict_refcnt, 0); #endif + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, "Not enough memory to generate traffic log\n"); /************************** * 2. Initializing PCI bus @@ -3034,6 +2897,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) test_bit(STATUS_RF_KILL_HW, &priv->status)); iwl_power_initialize(priv); + iwl_tt_initialize(priv); return 0; out_remove_sysfs: @@ -3057,6 +2921,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(priv->hw); + iwl_free_traffic_mem(priv); out: return err; } @@ -3086,6 +2951,8 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) iwl_down(priv); } + iwl_tt_exit(priv); + /* make sure we flush any pending irq or * tasklet for the driver */ @@ -3113,6 +2980,7 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) * until now... */ destroy_workqueue(priv->workqueue); priv->workqueue = NULL; + iwl_free_traffic_mem(priv); free_irq(priv->pci_dev->irq, priv); pci_disable_msi(priv->pci_dev); @@ -3163,15 +3031,12 @@ static struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)}, {IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)}, /* 6000/6050 Series */ - {IWL_PCI_DEVICE(0x0082, 0x1102, iwl6000_2ag_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1112, iwl6000_2ag_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1122, iwl6000_2ag_cfg)}, + {IWL_PCI_DEVICE(0x008D, PCI_ANY_ID, iwl6000h_2agn_cfg)}, + {IWL_PCI_DEVICE(0x008E, PCI_ANY_ID, iwl6000h_2agn_cfg)}, {IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, PCI_ANY_ID, iwl6000_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, PCI_ANY_ID, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)}, {IWL_PCI_DEVICE(0x0087, PCI_ANY_ID, iwl6050_2agn_cfg)}, {IWL_PCI_DEVICE(0x0088, PCI_ANY_ID, iwl6050_3agn_cfg)}, @@ -3231,3 +3096,11 @@ static void __exit iwl_exit(void) module_exit(iwl_exit); module_init(iwl_init); + +#ifdef CONFIG_IWLWIFI_DEBUG +module_param_named(debug50, iwl_debug_level, uint, 0444); +MODULE_PARM_DESC(debug50, "50XX debug output mask (deprecated)"); +module_param_named(debug, iwl_debug_level, uint, 0644); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif + diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c index a5d63672ad3..c4b565a2de9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-calib.c +++ b/drivers/net/wireless/iwlwifi/iwl-calib.c @@ -86,7 +86,7 @@ int iwl_send_calib_results(struct iwl_priv *priv) struct iwl_host_cmd hcmd = { .id = REPLY_PHY_CALIBRATION_CMD, - .meta.flags = CMD_SIZE_HUGE, + .flags = CMD_SIZE_HUGE, }; for (i = 0; i < IWL_CALIB_MAX; i++) { @@ -251,12 +251,7 @@ static int iwl_sens_energy_cck(struct iwl_priv *priv, /* increase energy threshold (reduce nrg value) * to decrease sensitivity */ - if (data->nrg_th_cck > - (ranges->max_nrg_cck + NRG_STEP_CCK)) - data->nrg_th_cck = data->nrg_th_cck - - NRG_STEP_CCK; - else - data->nrg_th_cck = ranges->max_nrg_cck; + data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; /* Else if we got fewer than desired, increase sensitivity */ } else if (false_alarms < min_false_alarms) { data->nrg_curr_state = IWL_FA_TOO_FEW; @@ -424,7 +419,7 @@ static int iwl_sensitivity_write(struct iwl_priv *priv) struct iwl_host_cmd cmd_out = { .id = SENSITIVITY_CMD, .len = sizeof(struct iwl_sensitivity_cmd), - .meta.flags = CMD_ASYNC, + .flags = CMD_ASYNC, .data = &cmd, }; @@ -857,7 +852,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, priv->cfg->ops->lib->update_chain_flags(priv); data->state = IWL_CHAIN_NOISE_DONE; - iwl_power_update_mode(priv, 0); + iwl_power_update_mode(priv, false); } EXPORT_SYMBOL(iwl_chain_noise_calibration); diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index c87033bf3ad..2c5c88fc38f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -283,7 +283,7 @@ struct iwl3945_power_per_rate { * 1) Dual stream (MIMO) * 2) Triple stream (MIMO) * - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps FAT duplicate data + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data * * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): * 3-0: 0xD) 6 Mbps @@ -320,11 +320,11 @@ struct iwl3945_power_per_rate { #define RATE_MCS_GF_POS 10 #define RATE_MCS_GF_MSK 0x400 -/* Bit 11: (1) Use 40Mhz FAT chnl width, (0) use 20 MHz legacy chnl width */ -#define RATE_MCS_FAT_POS 11 -#define RATE_MCS_FAT_MSK 0x800 +/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_HT40_POS 11 +#define RATE_MCS_HT40_MSK 0x800 -/* Bit 12: (1) Duplicate data on both 20MHz chnls. FAT (bit 11) must be set. */ +/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ #define RATE_MCS_DUP_POS 12 #define RATE_MCS_DUP_MSK 0x1000 @@ -459,7 +459,7 @@ struct iwl_init_alive_resp { /* calibration values from "initialize" uCode */ __le32 voltage; /* signed, higher value is lower voltage */ - __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for FAT channel*/ + __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for HT40 */ __le32 therm_r2[2]; /* signed */ __le32 therm_r3[2]; /* signed */ __le32 therm_r4[2]; /* signed */ @@ -610,7 +610,7 @@ enum { #define RXON_FLG_HT_OPERATING_MODE_POS (23) #define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) -#define RXON_FLG_FAT_PROT_MSK cpu_to_le32(0x2 << 23) +#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) #define RXON_FLG_CHANNEL_MODE_POS (25) #define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) @@ -765,6 +765,8 @@ struct iwl5000_rxon_assoc_cmd { } __attribute__ ((packed)); #define IWL_CONN_MAX_LISTEN_INTERVAL 10 +#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ +#define IWL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */ /* * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) @@ -884,12 +886,11 @@ struct iwl_qosparam_cmd { #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2); #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8); -#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8); #define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) #define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) #define STA_FLG_MAX_AGG_SIZE_POS (19) #define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) -#define STA_FLG_FAT_EN_MSK cpu_to_le32(1 << 21) +#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) #define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) #define STA_FLG_AGG_MPDU_DENSITY_POS (23) #define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) @@ -1154,6 +1155,7 @@ struct iwl_wep_cmd { #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) #define RX_RES_PHY_FLAGS_ANTENNA_MSK cpu_to_le16(0xf0) +#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) #define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) @@ -1922,7 +1924,7 @@ struct iwl_link_qual_general_params { #define LINK_QUAL_AGG_DISABLE_START_MIN (0) #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (31) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (64) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) /** @@ -1982,10 +1984,10 @@ struct iwl_link_qual_agg_params { * a) Use this same initial rate for first 3 entries. * b) Find next lower available rate using same mode (SISO or MIMO), * use for next 3 entries. If no lower rate available, switch to - * legacy mode (no FAT channel, no MIMO, no short guard interval). + * legacy mode (no HT40 channel, no MIMO, no short guard interval). * c) If using MIMO, set command's mimo_delimiter to number of entries * using MIMO (3 or 6). - * d) After trying 2 HT rates, switch to legacy mode (no FAT channel, + * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, * no MIMO, no short guard interval), at the next lower bit rate * (e.g. if second HT bit rate was 54, try 48 legacy), and follow * legacy procedure for remaining table entries. @@ -2311,15 +2313,22 @@ struct iwl_spectrum_notification { * PM allow: * bit 0 - '0' Driver not allow power management * '1' Driver allow PM (use rest of parameters) + * * uCode send sleep notifications: * bit 1 - '0' Don't send sleep notification * '1' send sleep notification (SEND_PM_NOTIFICATION) + * * Sleep over DTIM * bit 2 - '0' PM have to walk up every DTIM * '1' PM could sleep over DTIM till listen Interval. + * * PCI power managed * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) * '1' !(PCI_CFG_LINK_CTRL & 0x1) + * + * Fast PD + * bit 4 - '1' Put radio to sleep when receiving frame for others + * * Force sleep Modes * bit 31/30- '00' use both mac/xtal sleeps * '01' force Mac sleep @@ -2411,6 +2420,13 @@ struct iwl_ct_kill_config { __le32 critical_temperature_R; } __attribute__ ((packed)); +/* 1000, and 6x00 */ +struct iwl_ct_kill_throttling_config { + __le32 critical_temperature_exit; + __le32 reserved; + __le32 critical_temperature_enter; +} __attribute__ ((packed)); + /****************************************************************************** * (8) * Scan Commands, Responses, Notifications: @@ -2913,6 +2929,20 @@ struct statistics_rx { struct statistics_rx_ht_phy ofdm_ht; } __attribute__ ((packed)); +/** + * struct statistics_tx_power - current tx power + * + * @ant_a: current tx power on chain a in 1/2 dB step + * @ant_b: current tx power on chain b in 1/2 dB step + * @ant_c: current tx power on chain c in 1/2 dB step + */ +struct statistics_tx_power { + u8 ant_a; + u8 ant_b; + u8 ant_c; + u8 reserved; +} __attribute__ ((packed)); + struct statistics_tx_non_phy_agg { __le32 ba_timeout; __le32 ba_reschedule_frames; @@ -2924,8 +2954,6 @@ struct statistics_tx_non_phy_agg { __le32 underrun; __le32 bt_prio_kill; __le32 rx_ba_rsp_cnt; - __le32 reserved2; - __le32 reserved3; } __attribute__ ((packed)); struct statistics_tx { @@ -2944,6 +2972,8 @@ struct statistics_tx { __le32 cts_timeout_collision; __le32 ack_or_ba_timeout_collision; struct statistics_tx_non_phy_agg agg; + struct statistics_tx_power tx_power; + __le32 reserved1; } __attribute__ ((packed)); @@ -3008,7 +3038,7 @@ struct iwl_statistics_cmd { * one channel that has just been scanned. */ #define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) -#define STATISTICS_REPLY_FLG_FAT_MODE_MSK cpu_to_le32(0x8) +#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) struct iwl3945_notif_statistics { __le32 flag; @@ -3465,7 +3495,7 @@ struct iwl_wimax_coex_cmd { *****************************************************************************/ struct iwl_rx_packet { - __le32 len; + __le32 len_n_flags; struct iwl_cmd_header hdr; union { struct iwl3945_rx_frame rx_frame; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 18b135f510e..0bfd4e91813 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -59,6 +59,9 @@ MODULE_LICENSE("GPL"); IWL_RATE_##pp##M_INDEX, \ IWL_RATE_##np##M_INDEX } +u32 iwl_debug_level; +EXPORT_SYMBOL(iwl_debug_level); + static irqreturn_t iwl_isr(int irq, void *data); /* @@ -102,7 +105,7 @@ void iwl_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, r->flags |= IEEE80211_TX_RC_MCS; if (rate_n_flags & RATE_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - if (rate_n_flags & RATE_MCS_FAT_MSK) + if (rate_n_flags & RATE_MCS_HT40_MSK) r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (rate_n_flags & RATE_MCS_DUP_MSK) r->flags |= IEEE80211_TX_RC_DUP_DATA; @@ -391,13 +394,14 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, ht_info->ht_supported = true; - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + if (priv->cfg->ht_greenfield_support) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & (WLAN_HT_CAP_SM_PS_DISABLED << 2)); max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (priv->hw_params.fat_channel & BIT(band)) { + if (priv->hw_params.ht40_channel & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; ht_info->mcs.rx_mask[4] = 0x01; @@ -537,17 +541,14 @@ int iwlcore_init_geos(struct iwl_priv *priv) if (ch->flags & EEPROM_CHANNEL_RADAR) geo_ch->flags |= IEEE80211_CHAN_RADAR; - geo_ch->flags |= ch->fat_extension_channel; + geo_ch->flags |= ch->ht40_extension_channel; - if (ch->max_power_avg > priv->tx_power_channel_lmt) - priv->tx_power_channel_lmt = ch->max_power_avg; + if (ch->max_power_avg > priv->tx_power_device_lmt) + priv->tx_power_device_lmt = ch->max_power_avg; } else { geo_ch->flags |= IEEE80211_CHAN_DISABLED; } - /* Save flags for reg domain usage */ - geo_ch->orig_flags = geo_ch->flags; - IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, geo_ch->center_freq, is_channel_a_band(ch) ? "5.2" : "2.4", @@ -604,16 +605,16 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, return 0; if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - return !(ch_info->fat_extension_channel & + return !(ch_info->ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) - return !(ch_info->fat_extension_channel & + return !(ch_info->ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); return 0; } -u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, +u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct ieee80211_sta_ht_cap *sta_ht_inf) { struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config; @@ -629,11 +630,72 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, if (!sta_ht_inf->ht_supported) return 0; } +#ifdef CONFIG_IWLWIFI_DEBUG + if (priv->disable_ht40) + return 0; +#endif return iwl_is_channel_extension(priv, priv->band, le16_to_cpu(priv->staging_rxon.channel), iwl_ht_conf->extension_chan_offset); } -EXPORT_SYMBOL(iwl_is_fat_tx_allowed); +EXPORT_SYMBOL(iwl_is_ht40_tx_allowed); + +static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) +{ + u16 new_val = 0; + u16 beacon_factor = 0; + + beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; + new_val = beacon_val / beacon_factor; + + if (!new_val) + new_val = max_beacon_val; + + return new_val; +} + +void iwl_setup_rxon_timing(struct iwl_priv *priv) +{ + u64 tsf; + s32 interval_tm, rem; + unsigned long flags; + struct ieee80211_conf *conf = NULL; + u16 beacon_int; + + conf = ieee80211_get_hw_conf(priv->hw); + + spin_lock_irqsave(&priv->lock, flags); + priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp); + priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); + + if (priv->iw_mode == NL80211_IFTYPE_STATION) { + beacon_int = priv->beacon_int; + priv->rxon_timing.atim_window = 0; + } else { + beacon_int = priv->vif->bss_conf.beacon_int; + + /* TODO: we need to get atim_window from upper stack + * for now we set to 0 */ + priv->rxon_timing.atim_window = 0; + } + + beacon_int = iwl_adjust_beacon_interval(beacon_int, + priv->hw_params.max_beacon_itrvl * 1024); + priv->rxon_timing.beacon_interval = cpu_to_le16(beacon_int); + + tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ + interval_tm = beacon_int * 1024; + rem = do_div(tsf, interval_tm); + priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + spin_unlock_irqrestore(&priv->lock, flags); + IWL_DEBUG_ASSOC(priv, + "beacon interval %d beacon timer %d beacon tim %d\n", + le16_to_cpu(priv->rxon_timing.beacon_interval), + le32_to_cpu(priv->rxon_timing.beacon_init_val), + le16_to_cpu(priv->rxon_timing.atim_window)); +} +EXPORT_SYMBOL(iwl_setup_rxon_timing); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt) { @@ -805,7 +867,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) if (!ht_info->is_ht) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | - RXON_FLG_FAT_PROT_MSK | + RXON_FLG_HT40_PROT_MSK | RXON_FLG_HT_PROT_MSK); return; } @@ -816,12 +878,12 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS); /* Set up channel bandwidth: - * 20 MHz only, 20/40 mixed or pure 40 if fat ok */ + * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ /* clear the HT channel mode before set the mode */ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - if (iwl_is_fat_tx_allowed(priv, NULL)) { - /* pure 40 fat */ + if (iwl_is_ht40_tx_allowed(priv, NULL)) { + /* pure ht40 */ if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; /* Note: control channel is opposite of extension channel */ @@ -1076,7 +1138,6 @@ void iwl_set_flags_for_band(struct iwl_priv *priv, priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK; } } -EXPORT_SYMBOL(iwl_set_flags_for_band); /* * initialize rxon structure with default values from eeprom @@ -1231,9 +1292,191 @@ static void iwl_print_rx_config_cmd(struct iwl_priv *priv) IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } -#endif + +static const char *desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STABLE", + "FH_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT" + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", + "UNKNOWN" +}; + +static const char *desc_lookup(int i) +{ + int max = ARRAY_SIZE(desc_lookup_text) - 1; + + if (i < 0 || i > max) + i = max; + + return desc_lookup_text[i]; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +static void iwl_dump_nic_error_log(struct iwl_priv *priv) +{ + u32 data2, line; + u32 desc, time, count, base, data1; + u32 blink1, blink2, ilink1, ilink2; + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.error_event_table_ptr); + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); + return; + } + + count = iwl_read_targ_mem(priv, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { + IWL_ERR(priv, "Start IWL Error Log Dump:\n"); + IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", + priv->status, count); + } + + desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32)); + blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32)); + blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32)); + ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32)); + ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32)); + data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32)); + data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32)); + line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); + time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); + + IWL_ERR(priv, "Desc Time " + "data1 data2 line\n"); + IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", + desc_lookup(desc), desc, time, data1, data2, line); + IWL_ERR(priv, "blink1 blink2 ilink1 ilink2\n"); + IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, + ilink1, ilink2); + +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) /** + * iwl_print_event_log - Dump error event log to syslog + * + */ +static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode) +{ + u32 i; + u32 base; /* SRAM byte address of event log header */ + u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + + if (num_events == 0) + return; + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + + if (mode == 0) + event_size = 2 * sizeof(u32); + else + event_size = 3 * sizeof(u32); + + ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + + /* "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. */ + for (i = 0; i < num_events; i++) { + ev = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + time = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + if (mode == 0) { + /* data, ev */ + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); + } else { + data = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } + } +} + +void iwl_dump_nic_event_log(struct iwl_priv *priv) +{ + u32 base; /* SRAM byte address of event log header */ + u32 capacity; /* event log capacity in # entries */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + u32 size; /* # entries that we'll print */ + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); + return; + } + + /* event log header */ + capacity = iwl_read_targ_mem(priv, base); + mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); + return; + } + + IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", + size, num_wraps); + + /* if uCode has wrapped back to top of log, start at the oldest entry, + * i.e the next one that uCode would fill. */ + if (num_wraps) + iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode); + /* (then/else) start at top of log */ + iwl_print_event_log(priv, 0, next_entry, mode); + +} +#endif +/** * iwl_irq_handle_error - called for HW or SW error interrupt from card */ void iwl_irq_handle_error(struct iwl_priv *priv) @@ -1245,7 +1488,7 @@ void iwl_irq_handle_error(struct iwl_priv *priv) clear_bit(STATUS_HCMD_ACTIVE, &priv->status); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_FW_ERRORS) { + if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) { iwl_dump_nic_error_log(priv); iwl_dump_nic_event_log(priv); iwl_print_rx_config_cmd(priv); @@ -1271,7 +1514,7 @@ EXPORT_SYMBOL(iwl_irq_handle_error); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct iwl_priv *priv = hw->priv; __le32 *filter_flags = &priv->staging_rxon.filter_flags; @@ -1325,7 +1568,9 @@ int iwl_setup_mac(struct iwl_priv *priv) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_SPECTRUM_MGMT; + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); @@ -1335,6 +1580,12 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Firmware does not support this */ hw->wiphy->disable_beacon_hints = true; + /* + * For now, disable PS by default because it affects + * RX performance significantly. + */ + hw->wiphy->ps_default = false; + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; /* we create the 802.11 header and a zero-length SSID element */ hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2; @@ -1364,7 +1615,6 @@ EXPORT_SYMBOL(iwl_setup_mac); int iwl_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.sw_crypto = priv->cfg->mod_params->sw_crypto; priv->hw_params.max_rxq_size = RX_QUEUE_SIZE; priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; if (priv->cfg->mod_params->amsdu_size_8K) @@ -1373,6 +1623,8 @@ int iwl_set_hw_params(struct iwl_priv *priv) priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_4K; priv->hw_params.max_pkt_size = priv->hw_params.rx_buf_size - 256; + priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL; + if (priv->cfg->mod_params->disable_11n) priv->cfg->sku &= ~IWL_SKU_N; @@ -1419,9 +1671,10 @@ int iwl_init_drv(struct iwl_priv *priv) priv->qos_data.qos_cap.val = 0; priv->rates_mask = IWL_RATES_MASK; - /* If power management is turned on, default to CAM mode */ - priv->power_mode = IWL_POWER_MODE_CAM; - priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MAX; + /* Set the tx_power_user_lmt to the lowest power level + * this value will get overwritten by channel max power avg + * from eeprom */ + priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN; ret = iwl_init_channel_map(priv); if (ret) { @@ -1448,6 +1701,8 @@ EXPORT_SYMBOL(iwl_init_drv); int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) { int ret = 0; + s8 prev_tx_power = priv->tx_power_user_lmt; + if (tx_power < IWL_TX_POWER_TARGET_POWER_MIN) { IWL_WARN(priv, "Requested user TXPOWER %d below lower limit %d.\n", tx_power, @@ -1455,25 +1710,37 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) return -EINVAL; } - if (tx_power > IWL_TX_POWER_TARGET_POWER_MAX) { - IWL_WARN(priv, "Requested user TXPOWER %d above upper limit %d.\n", - tx_power, - IWL_TX_POWER_TARGET_POWER_MAX); + if (tx_power > priv->tx_power_device_lmt) { + IWL_WARN(priv, + "Requested user TXPOWER %d above upper limit %d.\n", + tx_power, priv->tx_power_device_lmt); return -EINVAL; } if (priv->tx_power_user_lmt != tx_power) force = true; - priv->tx_power_user_lmt = tx_power; - /* if nic is not up don't send command */ - if (!iwl_is_ready_rf(priv)) - return ret; - - if (force && priv->cfg->ops->lib->send_tx_power) - ret = priv->cfg->ops->lib->send_tx_power(priv); + if (iwl_is_ready_rf(priv)) { + priv->tx_power_user_lmt = tx_power; + if (force && priv->cfg->ops->lib->send_tx_power) + ret = priv->cfg->ops->lib->send_tx_power(priv); + else if (!priv->cfg->ops->lib->send_tx_power) + ret = -EOPNOTSUPP; + /* + * if fail to set tx_power, restore the orig. tx power + */ + if (ret) + priv->tx_power_user_lmt = prev_tx_power; + } + /* + * Even this is an async host command, the command + * will always report success from uCode + * So once driver can placing the command into the queue + * successfully, driver can use priv->tx_power_user_lmt + * to reflect the current tx power + */ return ret; } EXPORT_SYMBOL(iwl_set_tx_power); @@ -1487,31 +1754,6 @@ void iwl_uninit_drv(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_uninit_drv); - -void iwl_disable_interrupts(struct iwl_priv *priv) -{ - clear_bit(STATUS_INT_ENABLED, &priv->status); - - /* disable interrupts from uCode/NIC to host */ - iwl_write32(priv, CSR_INT_MASK, 0x00000000); - - /* acknowledge/clear/reset any interrupts still pending - * from uCode or flow handler (Rx/Tx DMA) */ - iwl_write32(priv, CSR_INT, 0xffffffff); - iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff); - IWL_DEBUG_ISR(priv, "Disabled interrupts\n"); -} -EXPORT_SYMBOL(iwl_disable_interrupts); - -void iwl_enable_interrupts(struct iwl_priv *priv) -{ - IWL_DEBUG_ISR(priv, "Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &priv->status); - iwl_write32(priv, CSR_INT_MASK, priv->inta_mask); -} -EXPORT_SYMBOL(iwl_enable_interrupts); - - #define ICT_COUNT (PAGE_SIZE/sizeof(u32)) /* Free dram table */ @@ -1581,7 +1823,7 @@ int iwl_reset_ict(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); iwl_disable_interrupts(priv); - memset(&priv->ict_tbl[0],0, sizeof(u32) * ICT_COUNT); + memset(&priv->ict_tbl[0], 0, sizeof(u32) * ICT_COUNT); val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT; @@ -1659,13 +1901,13 @@ irqreturn_t iwl_isr_ict(int irq, void *data) /* read all entries that not 0 start with ict_index */ while (priv->ict_tbl[priv->ict_index]) { - val |= priv->ict_tbl[priv->ict_index]; + val |= le32_to_cpu(priv->ict_tbl[priv->ict_index]); IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n", - priv->ict_index, - priv->ict_tbl[priv->ict_index]); + priv->ict_index, + le32_to_cpu(priv->ict_tbl[priv->ict_index])); priv->ict_tbl[priv->ict_index] = 0; priv->ict_index = iwl_queue_inc_wrap(priv->ict_index, - ICT_COUNT); + ICT_COUNT); } @@ -1745,7 +1987,7 @@ static irqreturn_t iwl_isr(int irq, void *data) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, " "fh 0x%08x\n", inta, inta_mask, inta_fh); @@ -1852,7 +2094,7 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags) u32 stat_flags = 0; struct iwl_host_cmd cmd = { .id = REPLY_STATISTICS_CMD, - .meta.flags = flags, + .flags = flags, .len = sizeof(stat_flags), .data = (u8 *) &stat_flags, }; @@ -1984,194 +2226,10 @@ int iwl_verify_ucode(struct iwl_priv *priv) EXPORT_SYMBOL(iwl_verify_ucode); -static const char *desc_lookup_text[] = { - "OK", - "FAIL", - "BAD_PARAM", - "BAD_CHECKSUM", - "NMI_INTERRUPT_WDG", - "SYSASSERT", - "FATAL_ERROR", - "BAD_COMMAND", - "HW_ERROR_TUNE_LOCK", - "HW_ERROR_TEMPERATURE", - "ILLEGAL_CHAN_FREQ", - "VCC_NOT_STABLE", - "FH_ERROR", - "NMI_INTERRUPT_HOST", - "NMI_INTERRUPT_ACTION_PT", - "NMI_INTERRUPT_UNKNOWN", - "UCODE_VERSION_MISMATCH", - "HW_ERROR_ABS_LOCK", - "HW_ERROR_CAL_LOCK_FAIL", - "NMI_INTERRUPT_INST_ACTION_PT", - "NMI_INTERRUPT_DATA_ACTION_PT", - "NMI_TRM_HW_ER", - "NMI_INTERRUPT_TRM", - "NMI_INTERRUPT_BREAK_POINT" - "DEBUG_0", - "DEBUG_1", - "DEBUG_2", - "DEBUG_3", - "UNKNOWN" -}; - -static const char *desc_lookup(int i) -{ - int max = ARRAY_SIZE(desc_lookup_text) - 1; - - if (i < 0 || i > max) - i = max; - - return desc_lookup_text[i]; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -void iwl_dump_nic_error_log(struct iwl_priv *priv) -{ - u32 data2, line; - u32 desc, time, count, base, data1; - u32 blink1, blink2, ilink1, ilink2; - - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.error_event_table_ptr); - - if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); - return; - } - - count = iwl_read_targ_mem(priv, base); - - if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { - IWL_ERR(priv, "Start IWL Error Log Dump:\n"); - IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", - priv->status, count); - } - - desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32)); - blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32)); - blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32)); - ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32)); - ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32)); - data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32)); - data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32)); - line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); - time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); - - IWL_ERR(priv, "Desc Time " - "data1 data2 line\n"); - IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", - desc_lookup(desc), desc, time, data1, data2, line); - IWL_ERR(priv, "blink1 blink2 ilink1 ilink2\n"); - IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, - ilink1, ilink2); - -} -EXPORT_SYMBOL(iwl_dump_nic_error_log); - -#define EVENT_START_OFFSET (4 * sizeof(u32)) - -/** - * iwl_print_event_log - Dump error event log to syslog - * - */ -static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode) -{ - u32 i; - u32 base; /* SRAM byte address of event log header */ - u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - - if (num_events == 0) - return; - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.log_event_table_ptr); - - if (mode == 0) - event_size = 2 * sizeof(u32); - else - event_size = 3 * sizeof(u32); - - ptr = base + EVENT_START_OFFSET + (start_idx * event_size); - - /* "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. */ - for (i = 0; i < num_events; i++) { - ev = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - time = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - if (mode == 0) { - /* data, ev */ - IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); - } else { - data = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - } - } -} - -void iwl_dump_nic_event_log(struct iwl_priv *priv) -{ - u32 base; /* SRAM byte address of event log header */ - u32 capacity; /* event log capacity in # entries */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - u32 size; /* # entries that we'll print */ - - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.log_event_table_ptr); - - if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); - return; - } - - /* event log header */ - capacity = iwl_read_targ_mem(priv, base); - mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); - num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); - next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); - - size = num_wraps ? capacity : next_entry; - - /* bail out if nothing in log */ - if (size == 0) { - IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); - return; - } - - IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", - size, num_wraps); - - /* if uCode has wrapped back to top of log, start at the oldest entry, - * i.e the next one that uCode would fill. */ - if (num_wraps) - iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode); - /* (then/else) start at top of log */ - iwl_print_event_log(priv, 0, next_entry, mode); - -} -EXPORT_SYMBOL(iwl_dump_nic_event_log); - void iwl_rf_kill_ct_config(struct iwl_priv *priv) { struct iwl_ct_kill_config cmd; + struct iwl_ct_kill_throttling_config adv_cmd; unsigned long flags; int ret = 0; @@ -2179,18 +2237,44 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv) iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); spin_unlock_irqrestore(&priv->lock, flags); + priv->thermal_throttle.ct_kill_toggle = false; + + switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_1000: + case CSR_HW_REV_TYPE_6x00: + case CSR_HW_REV_TYPE_6x50: + adv_cmd.critical_temperature_enter = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + adv_cmd.critical_temperature_exit = + cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); + + ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, + sizeof(adv_cmd), &adv_cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature enter is %d," + "exit is %d\n", + priv->hw_params.ct_kill_threshold, + priv->hw_params.ct_kill_exit_threshold); + break; + default: + cmd.critical_temperature_R = + cpu_to_le32(priv->hw_params.ct_kill_threshold); - cmd.critical_temperature_R = - cpu_to_le32(priv->hw_params.ct_kill_threshold); - - ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); - else - IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD succeeded, " - "critical temperature is %d\n", - cmd.critical_temperature_R); + ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature is %d\n", + priv->hw_params.ct_kill_threshold); + break; + } } EXPORT_SYMBOL(iwl_rf_kill_ct_config); @@ -2211,12 +2295,11 @@ int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag) .id = REPLY_CARD_STATE_CMD, .len = sizeof(u32), .data = &flags, - .meta.flags = meta_flag, + .flags = meta_flag, }; return iwl_send_cmd(priv, &cmd); } -EXPORT_SYMBOL(iwl_send_card_state); void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -2234,10 +2317,11 @@ void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " - "notification for %s:\n", - le32_to_cpu(pkt->len), get_cmd_string(pkt->hdr.cmd)); - iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len)); + "notification for %s:\n", len, + get_cmd_string(pkt->hdr.cmd)); + iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, len); } EXPORT_SYMBOL(iwl_rx_pm_debug_statistics_notif); @@ -2260,7 +2344,6 @@ void iwl_clear_isr_stats(struct iwl_priv *priv) { memset(&priv->isr_stats, 0, sizeof(priv->isr_stats)); } -EXPORT_SYMBOL(iwl_clear_isr_stats); int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) @@ -2333,39 +2416,10 @@ static void iwl_ht_conf(struct iwl_priv *priv, } ht_conf = &sta->ht_cap; - if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20) - iwl_conf->sgf |= HT_SHORT_GI_20MHZ; - if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40) - iwl_conf->sgf |= HT_SHORT_GI_40MHZ; - - iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD); - iwl_conf->max_amsdu_size = - !!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU); - - iwl_conf->supported_chan_width = - !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40); - - /* - * XXX: The HT configuration needs to be moved into iwl_mac_config() - * to be done there correctly. - */ - - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - if (conf_is_ht40_minus(&priv->hw->conf)) - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - else if (conf_is_ht40_plus(&priv->hw->conf)) - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - - /* If no above or below channel supplied disable FAT channel */ - if (iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_ABOVE && - iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW) - iwl_conf->supported_chan_width = 0; - iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2); memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16); - iwl_conf->tx_chan_width = iwl_conf->supported_chan_width != 0; iwl_conf->ht_protection = bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; iwl_conf->non_GF_STA_present = @@ -2492,7 +2546,6 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, if (bss_conf->assoc) { priv->assoc_id = bss_conf->aid; priv->beacon_int = bss_conf->beacon_int; - priv->power_data.dtim_period = bss_conf->dtim_period; priv->timestamp = bss_conf->timestamp; priv->assoc_capability = bss_conf->assoc_capability; @@ -2679,6 +2732,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) struct iwl_priv *priv = hw->priv; const struct iwl_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; + struct iwl_ht_info *ht_conf = &priv->current_ht_config; unsigned long flags = 0; int ret = 0; u16 ch; @@ -2720,10 +2774,32 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) goto set_ch_out; } - priv->current_ht_config.is_ht = conf_is_ht(conf); - spin_lock_irqsave(&priv->lock, flags); + /* Configure HT40 channels */ + ht_conf->is_ht = conf_is_ht(conf); + if (ht_conf->is_ht) { + if (conf_is_ht40_minus(conf)) { + ht_conf->extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ht_conf->supported_chan_width = + IWL_CHANNEL_WIDTH_40MHZ; + } else if (conf_is_ht40_plus(conf)) { + ht_conf->extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ht_conf->supported_chan_width = + IWL_CHANNEL_WIDTH_40MHZ; + } else { + ht_conf->extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ht_conf->supported_chan_width = + IWL_CHANNEL_WIDTH_20MHZ; + } + } else + ht_conf->supported_chan_width = IWL_CHANNEL_WIDTH_20MHZ; + /* Default to no protection. Protection mode will later be set + * from BSS config in iwl_ht_conf */ + ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not @@ -2742,13 +2818,10 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) iwl_set_rate(priv); } - if (changed & IEEE80211_CONF_CHANGE_PS && - priv->iw_mode == NL80211_IFTYPE_STATION) { - priv->power_data.power_disabled = - !(conf->flags & IEEE80211_CONF_PS); - ret = iwl_power_update_mode(priv, 0); + if (changed & IEEE80211_CONF_CHANGE_PS) { + ret = iwl_power_update_mode(priv, false); if (ret) - IWL_DEBUG_MAC80211(priv, "Error setting power level\n"); + IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -2881,6 +2954,248 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) } EXPORT_SYMBOL(iwl_mac_reset_tsf); +#ifdef CONFIG_IWLWIFI_DEBUGFS + +#define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES) + +void iwl_reset_traffic_log(struct iwl_priv *priv) +{ + priv->tx_traffic_idx = 0; + priv->rx_traffic_idx = 0; + if (priv->tx_traffic) + memset(priv->tx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); + if (priv->rx_traffic) + memset(priv->rx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); +} + +int iwl_alloc_traffic_mem(struct iwl_priv *priv) +{ + u32 traffic_size = IWL_TRAFFIC_DUMP_SIZE; + + if (iwl_debug_level & IWL_DL_TX) { + if (!priv->tx_traffic) { + priv->tx_traffic = + kzalloc(traffic_size, GFP_KERNEL); + if (!priv->tx_traffic) + return -ENOMEM; + } + } + if (iwl_debug_level & IWL_DL_RX) { + if (!priv->rx_traffic) { + priv->rx_traffic = + kzalloc(traffic_size, GFP_KERNEL); + if (!priv->rx_traffic) + return -ENOMEM; + } + } + iwl_reset_traffic_log(priv); + return 0; +} +EXPORT_SYMBOL(iwl_alloc_traffic_mem); + +void iwl_free_traffic_mem(struct iwl_priv *priv) +{ + kfree(priv->tx_traffic); + priv->tx_traffic = NULL; + + kfree(priv->rx_traffic); + priv->rx_traffic = NULL; +} +EXPORT_SYMBOL(iwl_free_traffic_mem); + +void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ + __le16 fc; + u16 len; + + if (likely(!(iwl_debug_level & IWL_DL_TX))) + return; + + if (!priv->tx_traffic) + return; + + fc = header->frame_control; + if (ieee80211_is_data(fc)) { + len = (length > IWL_TRAFFIC_ENTRY_SIZE) + ? IWL_TRAFFIC_ENTRY_SIZE : length; + memcpy((priv->tx_traffic + + (priv->tx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), + header, len); + priv->tx_traffic_idx = + (priv->tx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; + } +} +EXPORT_SYMBOL(iwl_dbg_log_tx_data_frame); + +void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ + __le16 fc; + u16 len; + + if (likely(!(iwl_debug_level & IWL_DL_RX))) + return; + + if (!priv->rx_traffic) + return; + + fc = header->frame_control; + if (ieee80211_is_data(fc)) { + len = (length > IWL_TRAFFIC_ENTRY_SIZE) + ? IWL_TRAFFIC_ENTRY_SIZE : length; + memcpy((priv->rx_traffic + + (priv->rx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), + header, len); + priv->rx_traffic_idx = + (priv->rx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; + } +} +EXPORT_SYMBOL(iwl_dbg_log_rx_data_frame); + +const char *get_mgmt_string(int cmd) +{ + switch (cmd) { + IWL_CMD(MANAGEMENT_ASSOC_REQ); + IWL_CMD(MANAGEMENT_ASSOC_RESP); + IWL_CMD(MANAGEMENT_REASSOC_REQ); + IWL_CMD(MANAGEMENT_REASSOC_RESP); + IWL_CMD(MANAGEMENT_PROBE_REQ); + IWL_CMD(MANAGEMENT_PROBE_RESP); + IWL_CMD(MANAGEMENT_BEACON); + IWL_CMD(MANAGEMENT_ATIM); + IWL_CMD(MANAGEMENT_DISASSOC); + IWL_CMD(MANAGEMENT_AUTH); + IWL_CMD(MANAGEMENT_DEAUTH); + IWL_CMD(MANAGEMENT_ACTION); + default: + return "UNKNOWN"; + + } +} + +const char *get_ctrl_string(int cmd) +{ + switch (cmd) { + IWL_CMD(CONTROL_BACK_REQ); + IWL_CMD(CONTROL_BACK); + IWL_CMD(CONTROL_PSPOLL); + IWL_CMD(CONTROL_RTS); + IWL_CMD(CONTROL_CTS); + IWL_CMD(CONTROL_ACK); + IWL_CMD(CONTROL_CFEND); + IWL_CMD(CONTROL_CFENDACK); + default: + return "UNKNOWN"; + + } +} + +void iwl_clear_tx_stats(struct iwl_priv *priv) +{ + memset(&priv->tx_stats, 0, sizeof(struct traffic_stats)); + +} + +void iwl_clear_rx_stats(struct iwl_priv *priv) +{ + memset(&priv->rx_stats, 0, sizeof(struct traffic_stats)); +} + +/* + * if CONFIG_IWLWIFI_DEBUGFS defined, iwl_update_stats function will + * record all the MGMT, CTRL and DATA pkt for both TX and Rx pass. + * Use debugFs to display the rx/rx_statistics + * if CONFIG_IWLWIFI_DEBUGFS not being defined, then no MGMT and CTRL + * information will be recorded, but DATA pkt still will be recorded + * for the reason of iwl_led.c need to control the led blinking based on + * number of tx and rx data. + * + */ +void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) +{ + struct traffic_stats *stats; + + if (is_tx) + stats = &priv->tx_stats; + else + stats = &priv->rx_stats; + + if (ieee80211_is_mgmt(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + stats->mgmt[MANAGEMENT_ASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): + stats->mgmt[MANAGEMENT_ASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): + stats->mgmt[MANAGEMENT_REASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): + stats->mgmt[MANAGEMENT_REASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): + stats->mgmt[MANAGEMENT_PROBE_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): + stats->mgmt[MANAGEMENT_PROBE_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BEACON): + stats->mgmt[MANAGEMENT_BEACON]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ATIM): + stats->mgmt[MANAGEMENT_ATIM]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DISASSOC): + stats->mgmt[MANAGEMENT_DISASSOC]++; + break; + case cpu_to_le16(IEEE80211_STYPE_AUTH): + stats->mgmt[MANAGEMENT_AUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + stats->mgmt[MANAGEMENT_DEAUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACTION): + stats->mgmt[MANAGEMENT_ACTION]++; + break; + } + } else if (ieee80211_is_ctl(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): + stats->ctrl[CONTROL_BACK_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BACK): + stats->ctrl[CONTROL_BACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PSPOLL): + stats->ctrl[CONTROL_PSPOLL]++; + break; + case cpu_to_le16(IEEE80211_STYPE_RTS): + stats->ctrl[CONTROL_RTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CTS): + stats->ctrl[CONTROL_CTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACK): + stats->ctrl[CONTROL_ACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFEND): + stats->ctrl[CONTROL_CFEND]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFENDACK): + stats->ctrl[CONTROL_CFENDACK]++; + break; + } + } else { + /* data */ + stats->data_cnt++; + stats->data_bytes += len; + } +} +EXPORT_SYMBOL(iwl_update_stats); +#endif + #ifdef CONFIG_PM int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index dabf663e36e..62d90364b61 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -83,6 +83,8 @@ struct iwl_cmd; #define IWL_SKU_A 0x2 #define IWL_SKU_N 0x8 +#define IWL_CMD(x) case x: return #x + struct iwl_hcmd_ops { int (*rxon_assoc)(struct iwl_priv *priv); int (*commit_rxon)(struct iwl_priv *priv); @@ -116,6 +118,17 @@ struct iwl_temp_ops { void (*set_ct_kill)(struct iwl_priv *priv); }; +struct iwl_ucode_ops { + u32 (*get_header_size)(u32); + u32 (*get_build)(const struct iwl_ucode_header *, u32); + u32 (*get_inst_size)(const struct iwl_ucode_header *, u32); + u32 (*get_data_size)(const struct iwl_ucode_header *, u32); + u32 (*get_init_size)(const struct iwl_ucode_header *, u32); + u32 (*get_init_data_size)(const struct iwl_ucode_header *, u32); + u32 (*get_boot_size)(const struct iwl_ucode_header *, u32); + u8 * (*get_data)(const struct iwl_ucode_header *, u32); +}; + struct iwl_lib_ops { /* set hw dependent parameters */ int (*set_hw_params)(struct iwl_priv *priv); @@ -171,6 +184,7 @@ struct iwl_lib_ops { }; struct iwl_ops { + const struct iwl_ucode_ops *ucode; const struct iwl_lib_ops *lib; const struct iwl_hcmd_ops *hcmd; const struct iwl_hcmd_utils_ops *utils; @@ -178,7 +192,6 @@ struct iwl_ops { struct iwl_mod_params { int sw_crypto; /* def: 0 = using hardware encryption */ - u32 debug; /* def: 0 = minimal debug log messages */ int disable_hw_scan; /* def: 0 = use h/w scan */ int num_of_queues; /* def: HW dependent */ int num_of_ampdu_queues;/* def: HW dependent */ @@ -195,6 +208,9 @@ struct iwl_mod_params { * filename is constructed as fw_name_pre<api>.ucode. * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. + * @pa_type: used by 6000 series only to identify the type of Power Amplifier + * @max_ll_items: max number of OTP blocks + * @shadow_ram_support: shadow support for OTP memory * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the @@ -215,6 +231,7 @@ struct iwl_mod_params { * iwl_hcmd_utils_ops etc. we accommodate different command structures * and flows between hardware versions (4965/5000) as well as their API * versions. + * */ struct iwl_cfg { const char *name; @@ -231,6 +248,10 @@ struct iwl_cfg { u8 valid_rx_ant; bool need_pll_cfg; bool use_isr_legacy; + enum iwl_pa_type pa_type; + const u16 max_ll_items; + const bool shadow_ram_support; + const bool ht_greenfield_support; }; /*************************** @@ -250,7 +271,7 @@ int iwl_full_rxon_required(struct iwl_priv *priv); void iwl_set_rxon_chain(struct iwl_priv *priv); int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch); void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info); -u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, +u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct ieee80211_sta_ht_cap *sta_ht_inf); void iwl_set_flags_for_band(struct iwl_priv *priv, enum ieee80211_band band); void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode); @@ -261,8 +282,7 @@ int iwl_set_decrypted_flag(struct iwl_priv *priv, void iwl_irq_handle_error(struct iwl_priv *priv); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + unsigned int *total_flags, u64 multicast); int iwl_hw_nic_init(struct iwl_priv *priv); int iwl_setup_mac(struct iwl_priv *priv); int iwl_set_hw_params(struct iwl_priv *priv); @@ -286,7 +306,55 @@ void iwl_config_ap(struct iwl_priv *priv); int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); void iwl_mac_reset_tsf(struct ieee80211_hw *hw); +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_alloc_traffic_mem(struct iwl_priv *priv); +void iwl_free_traffic_mem(struct iwl_priv *priv); +void iwl_reset_traffic_log(struct iwl_priv *priv); +void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header); +void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header); +const char *get_mgmt_string(int cmd); +const char *get_ctrl_string(int cmd); +void iwl_clear_tx_stats(struct iwl_priv *priv); +void iwl_clear_rx_stats(struct iwl_priv *priv); +void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, + u16 len); +#else +static inline int iwl_alloc_traffic_mem(struct iwl_priv *priv) +{ + return 0; +} +static inline void iwl_free_traffic_mem(struct iwl_priv *priv) +{ +} +static inline void iwl_reset_traffic_log(struct iwl_priv *priv) +{ +} +static inline void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ +} +static inline void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ +} +static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx, + __le16 fc, u16 len) +{ + struct traffic_stats *stats; + + if (is_tx) + stats = &priv->tx_stats; + else + stats = &priv->rx_stats; + if (ieee80211_is_data(fc)) { + /* data */ + stats->data_bytes += len; + } +} +#endif /***************************************************** * RX handlers. * **************************************************/ @@ -384,7 +452,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); -int iwl_scan_initiate(struct iwl_priv *priv); int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, const u8 *ie, int ie_len, int left); @@ -398,7 +465,6 @@ void iwl_bg_scan_check(struct work_struct *data); void iwl_bg_abort_scan(struct work_struct *work); void iwl_bg_scan_completed(struct work_struct *work); void iwl_setup_scan_deferred_work(struct iwl_priv *priv); -int iwl_send_scan_abort(struct iwl_priv *priv); /* For faster active scanning, scan will move to the next channel if fewer than * PLCP_QUIET_THRESH packets are heard on this channel within @@ -437,9 +503,9 @@ int __must_check iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data); int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, - int (*callback)(struct iwl_priv *priv, - struct iwl_cmd *cmd, - struct sk_buff *skb)); + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb)); int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); @@ -449,8 +515,6 @@ int iwl_send_card_state(struct iwl_priv *priv, u32 flags, /***************************************************** * PCI * *****************************************************/ -void iwl_disable_interrupts(struct iwl_priv *priv); -void iwl_enable_interrupts(struct iwl_priv *priv); irqreturn_t iwl_isr_legacy(int irq, void *data); int iwl_reset_ict(struct iwl_priv *priv); void iwl_disable_ict(struct iwl_priv *priv); @@ -474,7 +538,6 @@ int iwl_pci_resume(struct pci_dev *pdev); /***************************************************** * Error Handling Debugging ******************************************************/ -void iwl_dump_nic_error_log(struct iwl_priv *priv); void iwl_dump_nic_event_log(struct iwl_priv *priv); void iwl_clear_isr_stats(struct iwl_priv *priv); @@ -556,6 +619,7 @@ extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv, void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +void iwl_setup_rxon_timing(struct iwl_priv *priv); static inline int iwl_send_rxon_assoc(struct iwl_priv *priv) { return priv->cfg->ops->hcmd->rxon_assoc(priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index f03dae1b2f3..06437d13e73 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -91,7 +91,8 @@ #define CSR_EEPROM_GP (CSR_BASE+0x030) #define CSR_OTP_GP_REG (CSR_BASE+0x034) #define CSR_GIO_REG (CSR_BASE+0x03C) -#define CSR_GP_UCODE (CSR_BASE+0x044) +#define CSR_GP_UCODE_REG (CSR_BASE+0x048) +#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) #define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) #define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) #define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) @@ -245,6 +246,13 @@ #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) #define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) +/* GP Driver */ +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) + + /* GI Chicken Bits */ #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) #define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 2cf014f523b..cbc62904655 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -30,16 +30,23 @@ #define __iwl_debug_h__ struct iwl_priv; +extern u32 iwl_debug_level; #define IWL_ERR(p, f, a...) dev_err(&((p)->pci_dev->dev), f, ## a) #define IWL_WARN(p, f, a...) dev_warn(&((p)->pci_dev->dev), f, ## a) #define IWL_INFO(p, f, a...) dev_info(&((p)->pci_dev->dev), f, ## a) #define IWL_CRIT(p, f, a...) dev_crit(&((p)->pci_dev->dev), f, ## a) +#define iwl_print_hex_error(priv, p, len) \ +do { \ + print_hex_dump(KERN_ERR, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + #ifdef CONFIG_IWLWIFI_DEBUG #define IWL_DEBUG(__priv, level, fmt, args...) \ do { \ - if (__priv->debug_level & (level)) \ + if (iwl_get_debug_level(__priv) & (level)) \ dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \ "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ __func__ , ## args); \ @@ -47,7 +54,7 @@ do { \ #define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) \ do { \ - if ((__priv->debug_level & (level)) && net_ratelimit()) \ + if ((iwl_get_debug_level(__priv) & (level)) && net_ratelimit()) \ dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \ "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ __func__ , ## args); \ @@ -55,7 +62,7 @@ do { \ #define iwl_print_hex_dump(priv, level, p, len) \ do { \ - if (priv->debug_level & level) \ + if (iwl_get_debug_level(priv) & level) \ print_hex_dump(KERN_DEBUG, "iwl data: ", \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) @@ -65,23 +72,43 @@ struct iwl_debugfs { const char *name; struct dentry *dir_drv; struct dentry *dir_data; + struct dentry *dir_debug; struct dentry *dir_rf; struct dir_data_files { struct dentry *file_sram; struct dentry *file_nvm; struct dentry *file_stations; - struct dentry *file_rx_statistics; - struct dentry *file_tx_statistics; struct dentry *file_log_event; struct dentry *file_channels; struct dentry *file_status; struct dentry *file_interrupt; + struct dentry *file_qos; + struct dentry *file_thermal_throttling; +#ifdef CONFIG_IWLWIFI_LEDS + struct dentry *file_led; +#endif + struct dentry *file_disable_ht40; + struct dentry *file_sleep_level_override; + struct dentry *file_current_sleep_command; } dbgfs_data_files; struct dir_rf_files { struct dentry *file_disable_sensitivity; struct dentry *file_disable_chain_noise; struct dentry *file_disable_tx_power; } dbgfs_rf_files; + struct dir_debug_files { + struct dentry *file_rx_statistics; + struct dentry *file_tx_statistics; + struct dentry *file_traffic_log; + struct dentry *file_rx_queue; + struct dentry *file_tx_queue; + struct dentry *file_ucode_rx_stats; + struct dentry *file_ucode_tx_stats; + struct dentry *file_ucode_general_stats; + struct dentry *file_sensitivity; + struct dentry *file_chain_noise; + struct dentry *file_tx_power; + } dbgfs_debug_files; u32 sram_offset; u32 sram_len; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index ca00cc8ad4c..fb844859a44 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -38,7 +38,7 @@ #include "iwl-debug.h" #include "iwl-core.h" #include "iwl-io.h" - +#include "iwl-calib.h" /* create and remove of files */ #define DEBUGFS_ADD_DIR(name, parent) do { \ @@ -49,7 +49,8 @@ #define DEBUGFS_ADD_FILE(name, parent) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \ + debugfs_create_file(#name, S_IWUSR | S_IRUSR, \ + dbgfs->dir_##parent, priv, \ &iwl_dbgfs_##name##_ops); \ if (!(dbgfs->dbgfs_##parent##_files.file_##name)) \ goto err; \ @@ -57,7 +58,8 @@ #define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_bool(#name, 0644, dbgfs->dir_##parent, ptr); \ + debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + dbgfs->dir_##parent, ptr); \ if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \ || !dbgfs->dbgfs_##parent##_files.file_##name) \ goto err; \ @@ -65,7 +67,7 @@ #define DEBUGFS_ADD_X32(name, parent, ptr) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_x32(#name, 0444, dbgfs->dir_##parent, ptr); \ + debugfs_create_x32(#name, S_IRUSR, dbgfs->dir_##parent, ptr); \ if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \ || !dbgfs->dbgfs_##parent##_files.file_##name) \ goto err; \ @@ -124,18 +126,58 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - char buf[256]; + char *buf; int pos = 0; - const size_t bufsz = sizeof(buf); - pos += scnprintf(buf + pos, bufsz - pos, "mgmt: %u\n", - priv->tx_stats[0].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "ctrl: %u\n", - priv->tx_stats[1].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "data: %u\n", - priv->tx_stats[2].cnt); + int cnt; + ssize_t ret; + const size_t bufsz = 100 + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_mgmt_string(cnt), + priv->tx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_ctrl_string(cnt), + priv->tx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + priv->tx_stats.data_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + priv->tx_stats.data_bytes); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 clear_flag; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &clear_flag) != 1) + return -EFAULT; + if (clear_flag == 1) + iwl_clear_tx_stats(priv); + + return count; } static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, @@ -143,18 +185,59 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - char buf[256]; + char *buf; int pos = 0; - const size_t bufsz = sizeof(buf); + int cnt; + ssize_t ret; + const size_t bufsz = 100 + + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; - pos += scnprintf(buf + pos, bufsz - pos, "mgmt: %u\n", - priv->rx_stats[0].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "ctrl: %u\n", - priv->rx_stats[1].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "data: %u\n", - priv->rx_stats[2].cnt); + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_mgmt_string(cnt), + priv->rx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_ctrl_string(cnt), + priv->rx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + priv->rx_stats.data_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + priv->rx_stats.data_bytes); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 clear_flag; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &clear_flag) != 1) + return -EFAULT; + if (clear_flag == 1) + iwl_clear_rx_stats(priv); + return count; } #define BYTE1_MASK 0x000000ff; @@ -566,16 +649,982 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file, return count; } +static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0, i; + char buf[256]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + for (i = 0; i < AC_NUM; i++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tcw_min\tcw_max\taifsn\ttxop\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "AC[%d]\t%u\t%u\t%u\t%u\n", i, + priv->qos_data.def_qos_parm.ac[i].cw_min, + priv->qos_data.def_qos_parm.ac[i].cw_max, + priv->qos_data.def_qos_parm.ac[i].aifsn, + priv->qos_data.def_qos_parm.ac[i].edca_txop); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +#ifdef CONFIG_IWLWIFI_LEDS +static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char buf[256]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, + "allow blinking: %s\n", + (priv->allow_blinking) ? "True" : "False"); + if (priv->allow_blinking) { + pos += scnprintf(buf + pos, bufsz - pos, + "Led blinking rate: %u\n", + priv->last_blink_rate); + pos += scnprintf(buf + pos, bufsz - pos, + "Last blink time: %lu\n", + priv->last_blink_time); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} +#endif + +static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling Mode: %s\n", + tt->advanced_tt ? "Advance" : "Legacy"); + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling State: %d\n", + tt->state); + if (tt->advanced_tt) { + restriction = tt->restriction + tt->state; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx mode: %d\n", + restriction->tx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "Rx mode: %d\n", + restriction->rx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "HT mode: %d\n", + restriction->is_ht); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int ht40; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &ht40) != 1) + return -EFAULT; + if (!iwl_is_associated(priv)) + priv->disable_ht40 = ht40 ? true : false; + else { + IWL_ERR(priv, "Sta associated with AP - " + "Change to 40MHz channel support is not allowed\n"); + return -EINVAL; + } + + return count; +} + +static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, + "11n 40MHz Mode: %s\n", + priv->disable_ht40 ? "Disabled" : "Enabled"); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int value; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + /* + * Our users expect 0 to be "CAM", but 0 isn't actually + * valid here. However, let's not confuse them and present + * IWL_POWER_INDEX_1 as "1", not "0". + */ + if (value > 0) + value -= 1; + + if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) + return -EINVAL; + + priv->power_data.debug_sleep_level_override = value; + + iwl_power_update_mode(priv, false); + + return count; +} + +static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[10]; + int pos, value; + const size_t bufsz = sizeof(buf); + + /* see the write function */ + value = priv->power_data.debug_sleep_level_override; + if (value >= 0) + value += 1; + + pos = scnprintf(buf, bufsz, "%d\n", value); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[200]; + int pos = 0, i; + const size_t bufsz = sizeof(buf); + struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; + + pos += scnprintf(buf + pos, bufsz - pos, + "flags: %#.2x\n", le16_to_cpu(cmd->flags)); + pos += scnprintf(buf + pos, bufsz - pos, + "RX/TX timeout: %d/%d usec\n", + le32_to_cpu(cmd->rx_data_timeout), + le32_to_cpu(cmd->tx_data_timeout)); + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "sleep_interval[%d]: %d\n", i, + le32_to_cpu(cmd->sleep_interval[i])); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} DEBUGFS_READ_WRITE_FILE_OPS(sram); DEBUGFS_WRITE_FILE_OPS(log_event); DEBUGFS_READ_FILE_OPS(nvm); DEBUGFS_READ_FILE_OPS(stations); -DEBUGFS_READ_FILE_OPS(rx_statistics); -DEBUGFS_READ_FILE_OPS(tx_statistics); DEBUGFS_READ_FILE_OPS(channels); DEBUGFS_READ_FILE_OPS(status); DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(qos); +#ifdef CONFIG_IWLWIFI_LEDS +DEBUGFS_READ_FILE_OPS(led); +#endif +DEBUGFS_READ_FILE_OPS(thermal_throttling); +DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); +DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); +DEBUGFS_READ_FILE_OPS(current_sleep_command); + +static ssize_t iwl_dbgfs_traffic_log_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0, ofs = 0; + int cnt = 0, entry; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + struct iwl_rx_queue *rxq = &priv->rxq; + char *buf; + int bufsz = ((IWL_TRAFFIC_ENTRIES * IWL_TRAFFIC_ENTRY_SIZE * 64) * 2) + + (IWL_MAX_NUM_QUEUES * 32 * 8) + 400; + const u8 *ptr; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate buffer\n"); + return -ENOMEM; + } + pos += scnprintf(buf + pos, bufsz - pos, "Tx Queue\n"); + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + txq = &priv->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "q[%d]: read_ptr: %u, write_ptr: %u\n", + cnt, q->read_ptr, q->write_ptr); + } + if (priv->tx_traffic && (iwl_debug_level & IWL_DL_TX)) { + ptr = priv->tx_traffic; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx Traffic idx: %u\n", priv->tx_traffic_idx); + for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) { + for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16; + entry++, ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 2, + buf + pos, bufsz - pos, 0); + pos += strlen(buf); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } + } + + pos += scnprintf(buf + pos, bufsz - pos, "Rx Queue\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "read: %u, write: %u\n", + rxq->read, rxq->write); + + if (priv->rx_traffic && (iwl_debug_level & IWL_DL_RX)) { + ptr = priv->rx_traffic; + pos += scnprintf(buf + pos, bufsz - pos, + "Rx Traffic idx: %u\n", priv->rx_traffic_idx); + for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) { + for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16; + entry++, ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 2, + buf + pos, bufsz - pos, 0); + pos += strlen(buf); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_traffic_log_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int traffic_log; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &traffic_log) != 1) + return -EFAULT; + if (traffic_log == 0) + iwl_reset_traffic_log(priv); + + return count; +} + +static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + const size_t bufsz = sizeof(char) * 60 * IWL_MAX_NUM_QUEUES; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + txq = &priv->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u stop=%d" + " swq_id=%#.2x (ac %d/hwq %d)\n", + cnt, q->read_ptr, q->write_ptr, + !!test_bit(cnt, priv->queue_stopped), + txq->swq_id, + txq->swq_id & 0x80 ? txq->swq_id & 3 : + txq->swq_id, + txq->swq_id & 0x80 ? (txq->swq_id >> 2) & + 0x1f : txq->swq_id); + if (cnt >= 4) + continue; + /* for the ACs, display the stop count too */ + pos += scnprintf(buf + pos, bufsz - pos, + " stop-count: %d\n", + atomic_read(&priv->queue_stop_count[cnt])); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_rx_queue *rxq = &priv->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) + +static int iwl_dbgfs_statistics_flag(struct iwl_priv *priv, char *buf, + int bufsz) +{ + int p = 0; + + p += scnprintf(buf + p, bufsz - p, + "Statistics Flag(0x%X):\n", + le32_to_cpu(priv->statistics.flag)); + if (le32_to_cpu(priv->statistics.flag) & UCODE_STATISTICS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, + "\tOperational Frequency: %s\n", + (le32_to_cpu(priv->statistics.flag) & + UCODE_STATISTICS_FREQUENCY_MSK) + ? "2.4 GHz" : "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, + "\tTGj Narrow Band: %s\n", + (le32_to_cpu(priv->statistics.flag) & + UCODE_STATISTICS_NARROW_BAND_MSK) + ? "enabled" : "disabled"); + return p; +} + + +static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_rx_phy) * 20 + + sizeof(struct statistics_rx_non_phy) * 20 + + sizeof(struct statistics_rx_ht_phy) * 20 + 400; + ssize_t ret; + struct statistics_rx_phy *ofdm; + struct statistics_rx_phy *cck; + struct statistics_rx_non_phy *general; + struct statistics_rx_ht_phy *ht; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, + "Error sending statistics request: %zd\n", ret); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + ofdm = &priv->statistics.rx.ofdm; + cck = &priv->statistics.rx.cck; + general = &priv->statistics.rx.general; + ht = &priv->statistics.rx.ofdm_ht; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n", + le32_to_cpu(ofdm->ina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n", + le32_to_cpu(ofdm->fina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", + le32_to_cpu(ofdm->plcp_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", + le32_to_cpu(ofdm->crc32_err)); + pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", + le32_to_cpu(ofdm->overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", + le32_to_cpu(ofdm->early_overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", + le32_to_cpu(ofdm->crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n", + le32_to_cpu(ofdm->false_alarm_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n", + le32_to_cpu(ofdm->fina_sync_err_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n", + le32_to_cpu(ofdm->sfd_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n", + le32_to_cpu(ofdm->fina_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n", + le32_to_cpu(ofdm->unresponded_rts)); + pos += scnprintf(buf + pos, bufsz - pos, + "rxe_frame_limit_overrun: %u\n", + le32_to_cpu(ofdm->rxe_frame_limit_overrun)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n", + le32_to_cpu(ofdm->sent_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n", + le32_to_cpu(ofdm->sent_cts_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n", + le32_to_cpu(ofdm->sent_ba_rsp_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n", + le32_to_cpu(ofdm->dsp_self_kill)); + pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", + le32_to_cpu(ofdm->mh_format_err)); + pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n", + le32_to_cpu(ofdm->re_acq_main_rssi_sum)); + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - CCK:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n", + le32_to_cpu(cck->ina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n", + le32_to_cpu(cck->fina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", + le32_to_cpu(cck->plcp_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", + le32_to_cpu(cck->crc32_err)); + pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", + le32_to_cpu(cck->overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", + le32_to_cpu(cck->early_overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", + le32_to_cpu(cck->crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n", + le32_to_cpu(cck->false_alarm_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n", + le32_to_cpu(cck->fina_sync_err_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n", + le32_to_cpu(cck->sfd_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n", + le32_to_cpu(cck->fina_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n", + le32_to_cpu(cck->unresponded_rts)); + pos += scnprintf(buf + pos, bufsz - pos, + "rxe_frame_limit_overrun: %u\n", + le32_to_cpu(cck->rxe_frame_limit_overrun)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n", + le32_to_cpu(cck->sent_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n", + le32_to_cpu(cck->sent_cts_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n", + le32_to_cpu(cck->sent_ba_rsp_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n", + le32_to_cpu(cck->dsp_self_kill)); + pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", + le32_to_cpu(cck->mh_format_err)); + pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n", + le32_to_cpu(cck->re_acq_main_rssi_sum)); + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - GENERAL:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts: %u\n", + le32_to_cpu(general->bogus_cts)); + pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack: %u\n", + le32_to_cpu(general->bogus_ack)); + pos += scnprintf(buf + pos, bufsz - pos, "non_bssid_frames: %u\n", + le32_to_cpu(general->non_bssid_frames)); + pos += scnprintf(buf + pos, bufsz - pos, "filtered_frames: %u\n", + le32_to_cpu(general->filtered_frames)); + pos += scnprintf(buf + pos, bufsz - pos, "non_channel_beacons: %u\n", + le32_to_cpu(general->non_channel_beacons)); + pos += scnprintf(buf + pos, bufsz - pos, "channel_beacons: %u\n", + le32_to_cpu(general->channel_beacons)); + pos += scnprintf(buf + pos, bufsz - pos, "num_missed_bcon: %u\n", + le32_to_cpu(general->num_missed_bcon)); + pos += scnprintf(buf + pos, bufsz - pos, + "adc_rx_saturation_time: %u\n", + le32_to_cpu(general->adc_rx_saturation_time)); + pos += scnprintf(buf + pos, bufsz - pos, + "ina_detection_search_time: %u\n", + le32_to_cpu(general->ina_detection_search_time)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_a: %u\n", + le32_to_cpu(general->beacon_silence_rssi_a)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_b: %u\n", + le32_to_cpu(general->beacon_silence_rssi_b)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_c: %u\n", + le32_to_cpu(general->beacon_silence_rssi_c)); + pos += scnprintf(buf + pos, bufsz - pos, + "interference_data_flag: %u\n", + le32_to_cpu(general->interference_data_flag)); + pos += scnprintf(buf + pos, bufsz - pos, "channel_load: %u\n", + le32_to_cpu(general->channel_load)); + pos += scnprintf(buf + pos, bufsz - pos, "dsp_false_alarms: %u\n", + le32_to_cpu(general->dsp_false_alarms)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_a: %u\n", + le32_to_cpu(general->beacon_rssi_a)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_b: %u\n", + le32_to_cpu(general->beacon_rssi_b)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_c: %u\n", + le32_to_cpu(general->beacon_rssi_c)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_a: %u\n", + le32_to_cpu(general->beacon_energy_a)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_b: %u\n", + le32_to_cpu(general->beacon_energy_b)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_c: %u\n", + le32_to_cpu(general->beacon_energy_c)); + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", + le32_to_cpu(ht->plcp_err)); + pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", + le32_to_cpu(ht->overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", + le32_to_cpu(ht->early_overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", + le32_to_cpu(ht->crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", + le32_to_cpu(ht->crc32_err)); + pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", + le32_to_cpu(ht->mh_format_err)); + pos += scnprintf(buf + pos, bufsz - pos, "agg_crc32_good: %u\n", + le32_to_cpu(ht->agg_crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "agg_mpdu_cnt: %u\n", + le32_to_cpu(ht->agg_mpdu_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt: %u\n", + le32_to_cpu(ht->agg_cnt)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_tx) * 24) + 250; + ssize_t ret; + struct statistics_tx *tx; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, + "Error sending statistics request: %zd\n", ret); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + tx = &priv->statistics.tx; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Tx:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "preamble: %u\n", + le32_to_cpu(tx->preamble_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "rx_detected_cnt: %u\n", + le32_to_cpu(tx->rx_detected_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_defer_cnt: %u\n", + le32_to_cpu(tx->bt_prio_defer_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_kill_cnt: %u\n", + le32_to_cpu(tx->bt_prio_kill_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "few_bytes_cnt: %u\n", + le32_to_cpu(tx->few_bytes_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout: %u\n", + le32_to_cpu(tx->cts_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "ack_timeout: %u\n", + le32_to_cpu(tx->ack_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "expected_ack_cnt: %u\n", + le32_to_cpu(tx->expected_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "actual_ack_cnt: %u\n", + le32_to_cpu(tx->actual_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "dump_msdu_cnt: %u\n", + le32_to_cpu(tx->dump_msdu_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, + "burst_abort_next_frame_mismatch_cnt: %u\n", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, + "burst_abort_missing_next_frame_cnt: %u\n", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout_collision: %u\n", + le32_to_cpu(tx->cts_timeout_collision)); + pos += scnprintf(buf + pos, bufsz - pos, + "ack_or_ba_timeout_collision: %u\n", + le32_to_cpu(tx->ack_or_ba_timeout_collision)); + pos += scnprintf(buf + pos, bufsz - pos, "agg ba_timeout: %u\n", + le32_to_cpu(tx->agg.ba_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, + "agg ba_reschedule_frames: %u\n", + le32_to_cpu(tx->agg.ba_reschedule_frames)); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_agg_frame_cnt: %u\n", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_no_agg: %u\n", + le32_to_cpu(tx->agg.scd_query_no_agg)); + pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_agg: %u\n", + le32_to_cpu(tx->agg.scd_query_agg)); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_mismatch: %u\n", + le32_to_cpu(tx->agg.scd_query_mismatch)); + pos += scnprintf(buf + pos, bufsz - pos, "agg frame_not_ready: %u\n", + le32_to_cpu(tx->agg.frame_not_ready)); + pos += scnprintf(buf + pos, bufsz - pos, "agg underrun: %u\n", + le32_to_cpu(tx->agg.underrun)); + pos += scnprintf(buf + pos, bufsz - pos, "agg bt_prio_kill: %u\n", + le32_to_cpu(tx->agg.bt_prio_kill)); + pos += scnprintf(buf + pos, bufsz - pos, "agg rx_ba_rsp_cnt: %u\n", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_general) * 4 + 250; + ssize_t ret; + struct statistics_general *general; + struct statistics_dbg *dbg; + struct statistics_div *div; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, + "Error sending statistics request: %zd\n", ret); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + general = &priv->statistics.general; + dbg = &priv->statistics.general.dbg; + div = &priv->statistics.general.div; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_General:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "temperature: %u\n", + le32_to_cpu(general->temperature)); + pos += scnprintf(buf + pos, bufsz - pos, "temperature_m: %u\n", + le32_to_cpu(general->temperature_m)); + pos += scnprintf(buf + pos, bufsz - pos, "burst_check: %u\n", + le32_to_cpu(dbg->burst_check)); + pos += scnprintf(buf + pos, bufsz - pos, "burst_count: %u\n", + le32_to_cpu(dbg->burst_count)); + pos += scnprintf(buf + pos, bufsz - pos, "sleep_time: %u\n", + le32_to_cpu(general->sleep_time)); + pos += scnprintf(buf + pos, bufsz - pos, "slots_out: %u\n", + le32_to_cpu(general->slots_out)); + pos += scnprintf(buf + pos, bufsz - pos, "slots_idle: %u\n", + le32_to_cpu(general->slots_idle)); + pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp: %u\n", + le32_to_cpu(general->ttl_timestamp)); + pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a: %u\n", + le32_to_cpu(div->tx_on_a)); + pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b: %u\n", + le32_to_cpu(div->tx_on_b)); + pos += scnprintf(buf + pos, bufsz - pos, "exec_time: %u\n", + le32_to_cpu(div->exec_time)); + pos += scnprintf(buf + pos, bufsz - pos, "probe_time: %u\n", + le32_to_cpu(div->probe_time)); + pos += scnprintf(buf + pos, bufsz - pos, "rx_enable_counter: %u\n", + le32_to_cpu(general->rx_enable_counter)); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; + ssize_t ret; + struct iwl_sensitivity_data *data; + + data = &priv->sensitivity_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", + data->auto_corr_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc:\t\t %u\n", + data->auto_corr_ofdm_mrc); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", + data->auto_corr_ofdm_x1); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc_x1:\t\t %u\n", + data->auto_corr_ofdm_mrc_x1); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", + data->auto_corr_cck); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", + data->auto_corr_cck_mrc); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_ofdm:\t\t %u\n", + data->last_bad_plcp_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", + data->last_fa_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_cck:\t\t %u\n", + data->last_bad_plcp_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", + data->last_fa_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", + data->nrg_curr_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", + data->nrg_prev_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); + for (cnt = 0; cnt < 10; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_value[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); + for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_silence_rssi[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", + data->nrg_silence_ref); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", + data->nrg_energy_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", + data->nrg_silence_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", + data->nrg_th_cck); + pos += scnprintf(buf + pos, bufsz - pos, + "nrg_auto_corr_silence_diff:\t %u\n", + data->nrg_auto_corr_silence_diff); + pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", + data->num_in_cck_no_fa); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", + data->nrg_th_ofdm); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + + +static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; + ssize_t ret; + struct iwl_chain_noise_data *data; + + data = &priv->chain_noise_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", + data->active_chains); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", + data->chain_noise_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", + data->chain_noise_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", + data->chain_noise_c); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", + data->chain_signal_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", + data->chain_signal_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", + data->chain_signal_c); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", + data->beacon_count); + + pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->disconn_array[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->delta_gain_code[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", + data->radio_write); + pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", + data->state); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_tx_power_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[128]; + int pos = 0; + ssize_t ret; + const size_t bufsz = sizeof(buf); + struct statistics_tx *tx; + + if (!iwl_is_alive(priv)) + pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); + else { + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, "Error sending statistics request: %zd\n", + ret); + return -EAGAIN; + } + tx = &priv->statistics.tx; + if (tx->tx_power.ant_a || + tx->tx_power.ant_b || + tx->tx_power.ant_c) { + pos += scnprintf(buf + pos, bufsz - pos, + "tx power: (1/2 dB step)\n"); + if ((priv->cfg->valid_tx_ant & ANT_A) && + tx->tx_power.ant_a) + pos += scnprintf(buf + pos, bufsz - pos, + "\tantenna A: 0x%X\n", + tx->tx_power.ant_a); + if ((priv->cfg->valid_tx_ant & ANT_B) && + tx->tx_power.ant_b) + pos += scnprintf(buf + pos, bufsz - pos, + "\tantenna B: 0x%X\n", + tx->tx_power.ant_b); + if ((priv->cfg->valid_tx_ant & ANT_C) && + tx->tx_power.ant_c) + pos += scnprintf(buf + pos, bufsz - pos, + "\tantenna C: 0x%X\n", + tx->tx_power.ant_c); + } else + pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +DEBUGFS_READ_WRITE_FILE_OPS(rx_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(tx_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_READ_FILE_OPS(ucode_rx_stats); +DEBUGFS_READ_FILE_OPS(ucode_tx_stats); +DEBUGFS_READ_FILE_OPS(ucode_general_stats); +DEBUGFS_READ_FILE_OPS(sensitivity); +DEBUGFS_READ_FILE_OPS(chain_noise); +DEBUGFS_READ_FILE_OPS(tx_power); /* * Create the debugfs files and directories @@ -603,19 +1652,42 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_DIR(data, dbgfs->dir_drv); DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv); + DEBUGFS_ADD_DIR(debug, dbgfs->dir_drv); DEBUGFS_ADD_FILE(nvm, data); DEBUGFS_ADD_FILE(sram, data); DEBUGFS_ADD_FILE(log_event, data); DEBUGFS_ADD_FILE(stations, data); - DEBUGFS_ADD_FILE(rx_statistics, data); - DEBUGFS_ADD_FILE(tx_statistics, data); DEBUGFS_ADD_FILE(channels, data); DEBUGFS_ADD_FILE(status, data); DEBUGFS_ADD_FILE(interrupt, data); + DEBUGFS_ADD_FILE(qos, data); +#ifdef CONFIG_IWLWIFI_LEDS + DEBUGFS_ADD_FILE(led, data); +#endif + DEBUGFS_ADD_FILE(sleep_level_override, data); + DEBUGFS_ADD_FILE(current_sleep_command, data); + DEBUGFS_ADD_FILE(thermal_throttling, data); + DEBUGFS_ADD_FILE(disable_ht40, data); + DEBUGFS_ADD_FILE(rx_statistics, debug); + DEBUGFS_ADD_FILE(tx_statistics, debug); + DEBUGFS_ADD_FILE(traffic_log, debug); + DEBUGFS_ADD_FILE(rx_queue, debug); + DEBUGFS_ADD_FILE(tx_queue, debug); + DEBUGFS_ADD_FILE(tx_power, debug); + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { + DEBUGFS_ADD_FILE(ucode_rx_stats, debug); + DEBUGFS_ADD_FILE(ucode_tx_stats, debug); + DEBUGFS_ADD_FILE(ucode_general_stats, debug); + DEBUGFS_ADD_FILE(sensitivity, debug); + DEBUGFS_ADD_FILE(chain_noise, debug); + } DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal); DEBUGFS_ADD_BOOL(disable_chain_noise, rf, &priv->disable_chain_noise_cal); - DEBUGFS_ADD_BOOL(disable_tx_power, rf, &priv->disable_tx_power_cal); + if (((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) || + ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_3945)) + DEBUGFS_ADD_BOOL(disable_tx_power, rf, + &priv->disable_tx_power_cal); return 0; err: @@ -634,19 +1706,46 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) if (!priv->dbgfs) return; + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sleep_level_override); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_current_sleep_command); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_nvm); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_rx_statistics); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_tx_statistics); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sram); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_interrupt); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_qos); +#ifdef CONFIG_IWLWIFI_LEDS + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_led); +#endif + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_thermal_throttling); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_disable_ht40); DEBUGFS_REMOVE(priv->dbgfs->dir_data); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_statistics); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_statistics); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_traffic_log); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power); + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_ucode_rx_stats); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_ucode_tx_stats); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_ucode_general_stats); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_sensitivity); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_chain_noise); + } + DEBUGFS_REMOVE(priv->dbgfs->dir_debug); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_tx_power); + if (((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) || + ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_3945)) + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_tx_power); DEBUGFS_REMOVE(priv->dbgfs->dir_rf); DEBUGFS_REMOVE(priv->dbgfs->dir_drv); kfree(priv->dbgfs); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 650e20af20f..028d5059955 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -56,16 +56,19 @@ extern struct iwl_cfg iwl5350_agn_cfg; extern struct iwl_cfg iwl5100_bg_cfg; extern struct iwl_cfg iwl5100_abg_cfg; extern struct iwl_cfg iwl5150_agn_cfg; -extern struct iwl_cfg iwl6000_2ag_cfg; -extern struct iwl_cfg iwl6000_2agn_cfg; +extern struct iwl_cfg iwl6000h_2agn_cfg; +extern struct iwl_cfg iwl6000i_2agn_cfg; extern struct iwl_cfg iwl6000_3agn_cfg; extern struct iwl_cfg iwl6050_2agn_cfg; extern struct iwl_cfg iwl6050_3agn_cfg; extern struct iwl_cfg iwl1000_bgn_cfg; +struct iwl_tx_queue; + /* shared structures from iwl-5000.c */ extern struct iwl_mod_params iwl50_mod_params; extern struct iwl_ops iwl5000_ops; +extern struct iwl_ucode_ops iwl5000_ucode; extern struct iwl_lib_ops iwl5000_lib; extern struct iwl_hcmd_ops iwl5000_hcmd; extern struct iwl_hcmd_utils_ops iwl5000_hcmd_utils; @@ -78,9 +81,37 @@ extern void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info, __le32 *tx_flags); extern int iwl5000_calc_rssi(struct iwl_priv *priv, struct iwl_rx_phy_res *rx_resp); +extern int iwl5000_apm_init(struct iwl_priv *priv); +extern void iwl5000_apm_stop(struct iwl_priv *priv); +extern int iwl5000_apm_reset(struct iwl_priv *priv); +extern void iwl5000_nic_config(struct iwl_priv *priv); +extern u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv); +extern const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, + size_t offset); +extern void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + u16 byte_cnt); +extern void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, + struct iwl_tx_queue *txq); +extern int iwl5000_load_ucode(struct iwl_priv *priv); +extern void iwl5000_init_alive_start(struct iwl_priv *priv); +extern int iwl5000_alive_notify(struct iwl_priv *priv); +extern int iwl5000_hw_set_hw_params(struct iwl_priv *priv); +extern int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, + int tx_fifo, int sta_id, int tid, u16 ssn_idx); +extern int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, + u16 ssn_idx, u8 tx_fifo); +extern void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask); +extern void iwl5000_setup_deferred_work(struct iwl_priv *priv); +extern void iwl5000_rx_handler_setup(struct iwl_priv *priv); +extern int iwl5000_hw_valid_rtc_data_addr(u32 addr); +extern int iwl5000_send_tx_power(struct iwl_priv *priv); +extern void iwl5000_temperature(struct iwl_priv *priv); /* CT-KILL constants */ -#define CT_KILL_THRESHOLD 110 /* in Celsius */ +#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ +#define CT_KILL_THRESHOLD 114 /* in Celsius */ +#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ /* Default noise level to report when noise measurement is not available. * This may be because we're: @@ -119,6 +150,31 @@ struct iwl_rx_mem_buffer { struct list_head list; }; +/* defined below */ +struct iwl_device_cmd; + +struct iwl_cmd_meta { + /* only for SYNC commands, iff the reply skb is wanted */ + struct iwl_host_cmd *source; + /* + * only for ASYNC commands + * (which is somewhat stupid -- look at iwl-sta.c for instance + * which duplicates a bunch of code because the callback isn't + * invoked for SYNC commands, if it were and its result passed + * through it would be simpler...) + */ + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb); + + /* The CMD_SIZE_HUGE flag bit indicates that the command + * structure is stored at the end of the shared queue memory. */ + u32 flags; + + DECLARE_PCI_UNMAP_ADDR(mapping) + DECLARE_PCI_UNMAP_LEN(len) +}; + /* * Generic queue structure * @@ -146,7 +202,8 @@ struct iwl_tx_info { * struct iwl_tx_queue - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @bd: base of circular buffer of TFDs - * @cmd: array of command/Tx buffers + * @cmd: array of command/TX buffer pointers + * @meta: array of meta data for each command/tx buffer * @dma_addr_cmd: physical address of cmd/tx buffer array * @txb: array of per-TFD driver data * @need_update: indicates need to update read/write index @@ -161,7 +218,8 @@ struct iwl_tx_info { struct iwl_tx_queue { struct iwl_queue q; void *tfds; - struct iwl_cmd *cmd[TFD_TX_CMD_SLOTS]; + struct iwl_device_cmd **cmd; + struct iwl_cmd_meta *meta; struct iwl_tx_info *txb; u8 need_update; u8 sched_retry; @@ -219,8 +277,8 @@ struct iwl_channel_info { struct iwl4965_channel_tgd_info tgd; struct iwl4965_channel_tgh_info tgh; struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */ - struct iwl_eeprom_channel fat_eeprom; /* EEPROM regulatory limit for - * FAT channel */ + struct iwl_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for + * HT40 channel */ u8 channel; /* channel number */ u8 flags; /* flags copied from EEPROM */ @@ -233,13 +291,13 @@ struct iwl_channel_info { u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */ enum ieee80211_band band; - /* FAT channel info */ - s8 fat_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - s8 fat_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */ - s8 fat_min_power; /* always 0 */ - s8 fat_scan_power; /* (dBm) eeprom, direct scans, any rate */ - u8 fat_flags; /* flags copied from EEPROM */ - u8 fat_extension_channel; /* HT_IE_EXT_CHANNEL_* */ + /* HT40 channel info */ + s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + s8 ht40_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */ + s8 ht40_min_power; /* always 0 */ + s8 ht40_scan_power; /* (dBm) eeprom, direct scans, any rate */ + u8 ht40_flags; /* flags copied from EEPROM */ + u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ /* Radio/DSP gain settings for each "normal" data Tx rate. * These include, in addition to RF and DSP gain, a few fields for @@ -298,35 +356,16 @@ enum { CMD_WANT_SKB = (1 << 2), }; -struct iwl_cmd; -struct iwl_priv; - -struct iwl_cmd_meta { - struct iwl_cmd_meta *source; - union { - struct sk_buff *skb; - int (*callback)(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb); - } __attribute__ ((packed)) u; - - /* The CMD_SIZE_HUGE flag bit indicates that the command - * structure is stored at the end of the shared queue memory. */ - u32 flags; - DECLARE_PCI_UNMAP_ADDR(mapping) - DECLARE_PCI_UNMAP_LEN(len) -} __attribute__ ((packed)); - #define IWL_CMD_MAX_PAYLOAD 320 /** - * struct iwl_cmd + * struct iwl_device_cmd * * For allocation of the command and tx queues, this establishes the overall * size of the largest command we send to uCode, except for a scan command * (which is relatively huge; space is allocated separately). */ -struct iwl_cmd { - struct iwl_cmd_meta meta; /* driver data */ +struct iwl_device_cmd { struct iwl_cmd_header hdr; /* uCode API */ union { u32 flags; @@ -338,17 +377,20 @@ struct iwl_cmd { } __attribute__ ((packed)) cmd; } __attribute__ ((packed)); +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) + struct iwl_host_cmd { - u8 id; - u16 len; - struct iwl_cmd_meta meta; const void *data; + struct sk_buff *reply_skb; + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb); + u32 flags; + u16 len; + u8 id; }; -#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_cmd) - \ - sizeof(struct iwl_cmd_meta)) - /* * RX related structures and functions */ @@ -449,23 +491,25 @@ union iwl_ht_rate_supp { }; #define CFG_HT_RX_AMPDU_FACTOR_DEF (0x3) -#define CFG_HT_MPDU_DENSITY_2USEC (0x5) -#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_2USEC + +/* + * Maximal MPDU density for TX aggregation + * 4 - 2us density + * 5 - 4us density + * 6 - 8us density + * 7 - 16us density + */ +#define CFG_HT_MPDU_DENSITY_4USEC (0x5) +#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC struct iwl_ht_info { /* self configuration data */ u8 is_ht; u8 supported_chan_width; u8 sm_ps; - u8 is_green_field; - u8 sgf; /* HT_SHORT_GI_* short guard interval */ - u8 max_amsdu_size; - u8 ampdu_factor; - u8 mpdu_density; struct ieee80211_mcs_info mcs; /* BSS related data */ u8 extension_chan_offset; - u8 tx_chan_width; u8 ht_protection; u8 non_GF_STA_present; }; @@ -525,15 +569,29 @@ struct fw_desc { }; /* uCode file layout */ -struct iwl_ucode { - __le32 ver; /* major/minor/API/serial */ - __le32 inst_size; /* bytes of runtime instructions */ - __le32 data_size; /* bytes of runtime data */ - __le32 init_size; /* bytes of initialization instructions */ - __le32 init_data_size; /* bytes of initialization data */ - __le32 boot_size; /* bytes of bootstrap instructions */ - u8 data[0]; /* data in same order as "size" elements */ +struct iwl_ucode_header { + __le32 ver; /* major/minor/API/serial */ + union { + struct { + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v1; + struct { + __le32 build; /* build number */ + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v2; + } u; }; +#define UCODE_HEADER_SIZE(ver) ((ver) == 1 ? 24 : 28) struct iwl4965_ibss_seq { u8 mac[ETH_ALEN]; @@ -585,7 +643,7 @@ struct iwl_sensitivity_ranges { * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR * @max_stations: * @bcast_sta_id: - * @fat_channel: is 40MHz width possible in band 2.4 + * @ht40_channel: is 40MHz width possible in band 2.4 * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) * @sw_crypto: 0 for hw, 1 for sw * @max_xxx_size: for ucode uses @@ -609,12 +667,14 @@ struct iwl_hw_params { u32 max_pkt_size; u8 max_stations; u8 bcast_sta_id; - u8 fat_channel; - u8 sw_crypto; + u8 ht40_channel; + u8 max_beacon_itrvl; /* in 1024 ms */ u32 max_inst_size; u32 max_data_size; u32 max_bsm_size; u32 ct_kill_threshold; /* value in hw-dependent units */ + u32 ct_kill_exit_threshold; /* value in hw-dependent units */ + /* for 1000, 6000 series and up */ u32 calib_init_cfg; const struct iwl_sensitivity_ranges *sens; }; @@ -666,9 +726,6 @@ struct iwl_dma_ptr { size_t size; }; -#define HT_SHORT_GI_20MHZ (1 << 0) -#define HT_SHORT_GI_40MHZ (1 << 1) - #define IWL_CHANNEL_WIDTH_20MHZ 0 #define IWL_CHANNEL_WIDTH_40MHZ 1 @@ -809,6 +866,8 @@ struct iwl_chain_noise_data { #define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ #define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ +#define IWL_TRAFFIC_ENTRIES (256) +#define IWL_TRAFFIC_ENTRY_SIZE (64) enum { MEASUREMENT_READY = (1 << 0), @@ -820,6 +879,30 @@ enum iwl_nvm_type { NVM_DEVICE_TYPE_OTP, }; +/* + * Two types of OTP memory access modes + * IWL_OTP_ACCESS_ABSOLUTE - absolute address mode, + * based on physical memory addressing + * IWL_OTP_ACCESS_RELATIVE - relative address mode, + * based on logical memory addressing + */ +enum iwl_access_mode { + IWL_OTP_ACCESS_ABSOLUTE, + IWL_OTP_ACCESS_RELATIVE, +}; + +/** + * enum iwl_pa_type - Power Amplifier type + * @IWL_PA_SYSTEM: based on uCode configuration + * @IWL_PA_HYBRID: use both Internal and external PA + * @IWL_PA_INTERNAL: use Internal only + */ +enum iwl_pa_type { + IWL_PA_SYSTEM = 0, + IWL_PA_HYBRID = 1, + IWL_PA_INTERNAL = 2, +}; + /* interrupt statistics */ struct isr_statistics { u32 hw; @@ -836,6 +919,48 @@ struct isr_statistics { u32 unhandled; }; +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* management statistics */ +enum iwl_mgmt_stats { + MANAGEMENT_ASSOC_REQ = 0, + MANAGEMENT_ASSOC_RESP, + MANAGEMENT_REASSOC_REQ, + MANAGEMENT_REASSOC_RESP, + MANAGEMENT_PROBE_REQ, + MANAGEMENT_PROBE_RESP, + MANAGEMENT_BEACON, + MANAGEMENT_ATIM, + MANAGEMENT_DISASSOC, + MANAGEMENT_AUTH, + MANAGEMENT_DEAUTH, + MANAGEMENT_ACTION, + MANAGEMENT_MAX, +}; +/* control statistics */ +enum iwl_ctrl_stats { + CONTROL_BACK_REQ = 0, + CONTROL_BACK, + CONTROL_PSPOLL, + CONTROL_RTS, + CONTROL_CTS, + CONTROL_ACK, + CONTROL_CFEND, + CONTROL_CFENDACK, + CONTROL_MAX, +}; + +struct traffic_stats { + u32 mgmt[MANAGEMENT_MAX]; + u32 ctrl[CONTROL_MAX]; + u32 data_cnt; + u64 data_bytes; +}; +#else +struct traffic_stats { + u64 data_bytes; +}; +#endif + #define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */ struct iwl_priv { @@ -981,15 +1106,14 @@ struct iwl_priv { int last_rx_noise; /* From beacon statistics */ /* counts mgmt, ctl, and data packets */ - struct traffic_stats { - u32 cnt; - u64 bytes; - } tx_stats[3], rx_stats[3]; + struct traffic_stats tx_stats; + struct traffic_stats rx_stats; /* counts interrupts */ struct isr_statistics isr_stats; struct iwl_power_mgr power_data; + struct iwl_tt_mgmt thermal_throttle; struct iwl_notif_statistics statistics; unsigned long last_statistics_time; @@ -997,7 +1121,6 @@ struct iwl_priv { /* context information */ u16 rates_mask; - u32 power_mode; u8 bssid[ETH_ALEN]; u16 rts_threshold; u8 mac_addr[ETH_ALEN]; @@ -1047,7 +1170,7 @@ struct iwl_priv { struct iwl_hw_params hw_params; /* INT ICT Table */ - u32 *ict_tbl; + __le32 *ict_tbl; dma_addr_t ict_tbl_dma; dma_addr_t aligned_ict_tbl_dma; int ict_index; @@ -1076,6 +1199,9 @@ struct iwl_priv { struct work_struct report_work; struct work_struct request_scan; struct work_struct beacon_update; + struct work_struct tt_work; + struct work_struct ct_enter; + struct work_struct ct_exit; struct tasklet_struct irq_tasklet; @@ -1089,16 +1215,22 @@ struct iwl_priv { /* TX Power */ s8 tx_power_user_lmt; - s8 tx_power_channel_lmt; + s8 tx_power_device_lmt; #ifdef CONFIG_IWLWIFI_DEBUG /* debugging info */ - u32 debug_level; + u32 debug_level; /* per device debugging will override global + iwl_debug_level if set */ u32 framecnt_to_us; atomic_t restrict_refcnt; + bool disable_ht40; #ifdef CONFIG_IWLWIFI_DEBUGFS /* debugfs */ + u16 tx_traffic_idx; + u16 rx_traffic_idx; + u8 *tx_traffic; + u8 *rx_traffic; struct iwl_debugfs *dbgfs; #endif /* CONFIG_IWLWIFI_DEBUGFS */ #endif /* CONFIG_IWLWIFI_DEBUG */ @@ -1130,8 +1262,27 @@ static inline void iwl_txq_ctx_deactivate(struct iwl_priv *priv, int txq_id) #ifdef CONFIG_IWLWIFI_DEBUG const char *iwl_get_tx_fail_reason(u32 status); +/* + * iwl_get_debug_level: Return active debug level for device + * + * Using sysfs it is possible to set per device debug level. This debug + * level will be used if set, otherwise the global debug level which can be + * set via module parameter is used. + */ +static inline u32 iwl_get_debug_level(struct iwl_priv *priv) +{ + if (priv->debug_level) + return priv->debug_level; + else + return iwl_debug_level; +} #else static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } + +static inline u32 iwl_get_debug_level(struct iwl_priv *priv) +{ + return iwl_debug_level; +} #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 7d7554a2f34..3d2b93a61e6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -127,14 +127,86 @@ static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ 145, 149, 153, 157, 161, 165 }; -static const u8 iwl_eeprom_band_6[] = { /* 2.4 FAT channel */ +static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ 1, 2, 3, 4, 5, 6, 7 }; -static const u8 iwl_eeprom_band_7[] = { /* 5.2 FAT channel */ +static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 }; +/** + * struct iwl_txpwr_section: eeprom section information + * @offset: indirect address into eeprom image + * @count: number of "struct iwl_eeprom_enhanced_txpwr" in this section + * @band: band type for the section + * @is_common - true: common section, false: channel section + * @is_cck - true: cck section, false: not cck section + * @is_ht_40 - true: all channel in the section are HT40 channel, + * false: legacy or HT 20 MHz + * ignore if it is common section + * @iwl_eeprom_section_channel: channel array in the section, + * ignore if common section + */ +struct iwl_txpwr_section { + u32 offset; + u8 count; + enum ieee80211_band band; + bool is_common; + bool is_cck; + bool is_ht40; + u8 iwl_eeprom_section_channel[EEPROM_MAX_TXPOWER_SECTION_ELEMENTS]; +}; + +/** + * section 1 - 3 are regulatory tx power apply to all channels based on + * modulation: CCK, OFDM + * Band: 2.4GHz, 5.2GHz + * section 4 - 10 are regulatory tx power apply to specified channels + * For example: + * 1L - Channel 1 Legacy + * 1HT - Channel 1 HT + * (1,+1) - Channel 1 HT40 "_above_" + * + * Section 1: all CCK channels + * Section 2: all 2.4 GHz OFDM (Legacy, HT and HT40) channels + * Section 3: all 5.2 GHz OFDM (Legacy, HT and HT40) channels + * Section 4: 2.4 GHz 20MHz channels: 1L, 1HT, 2L, 2HT, 10L, 10HT, 11L, 11HT + * Section 5: 2.4 GHz 40MHz channels: (1,+1) (2,+1) (6,+1) (7,+1) (9,+1) + * Section 6: 5.2 GHz 20MHz channels: 36L, 64L, 100L, 36HT, 64HT, 100HT + * Section 7: 5.2 GHz 40MHz channels: (36,+1) (60,+1) (100,+1) + * Section 8: 2.4 GHz channel: 13L, 13HT + * Section 9: 2.4 GHz channel: 140L, 140HT + * Section 10: 2.4 GHz 40MHz channels: (132,+1) (44,+1) + * + */ +static const struct iwl_txpwr_section enhinfo[] = { + { EEPROM_LB_CCK_20_COMMON, 1, IEEE80211_BAND_2GHZ, true, true, false }, + { EEPROM_LB_OFDM_COMMON, 3, IEEE80211_BAND_2GHZ, true, false, false }, + { EEPROM_HB_OFDM_COMMON, 3, IEEE80211_BAND_5GHZ, true, false, false }, + { EEPROM_LB_OFDM_20_BAND, 8, IEEE80211_BAND_2GHZ, + false, false, false, + {1, 1, 2, 2, 10, 10, 11, 11 } }, + { EEPROM_LB_OFDM_HT40_BAND, 5, IEEE80211_BAND_2GHZ, + false, false, true, + { 1, 2, 6, 7, 9 } }, + { EEPROM_HB_OFDM_20_BAND, 6, IEEE80211_BAND_5GHZ, + false, false, false, + { 36, 64, 100, 36, 64, 100 } }, + { EEPROM_HB_OFDM_HT40_BAND, 3, IEEE80211_BAND_5GHZ, + false, false, true, + { 36, 60, 100 } }, + { EEPROM_LB_OFDM_20_CHANNEL_13, 2, IEEE80211_BAND_2GHZ, + false, false, false, + { 13, 13 } }, + { EEPROM_HB_OFDM_20_CHANNEL_140, 2, IEEE80211_BAND_5GHZ, + false, false, false, + { 140, 140 } }, + { EEPROM_HB_OFDM_HT40_BAND_1, 2, IEEE80211_BAND_5GHZ, + false, false, true, + { 132, 44 } }, +}; + /****************************************************************************** * * EEPROM related functions @@ -152,6 +224,19 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv) } EXPORT_SYMBOL(iwlcore_eeprom_verify_signature); +static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode) +{ + u32 otpgp; + + otpgp = iwl_read32(priv, CSR_OTP_GP_REG); + if (mode == IWL_OTP_ACCESS_ABSOLUTE) + iwl_clear_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); + else + iwl_set_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); +} + static int iwlcore_get_nvm_type(struct iwl_priv *priv) { u32 otpgp; @@ -159,6 +244,9 @@ static int iwlcore_get_nvm_type(struct iwl_priv *priv) /* OTP only valid for CP/PP and after */ switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_NONE: + IWL_ERR(priv, "Unknown hardware type\n"); + return -ENOENT; case CSR_HW_REV_TYPE_3945: case CSR_HW_REV_TYPE_4965: case CSR_HW_REV_TYPE_5300: @@ -249,6 +337,124 @@ static int iwl_init_otp_access(struct iwl_priv *priv) return ret; } +static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data) +{ + int ret = 0; + u32 r; + u32 otpgp; + + _iwl_write32(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(priv, "Time out reading OTP[%d]\n", addr); + return ret; + } + r = _iwl_read_direct32(priv, CSR_EEPROM_REG); + /* check for ECC errors: */ + otpgp = iwl_read32(priv, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { + /* stop in this case */ + /* set the uncorrectable OTP ECC bit for acknowledgement */ + iwl_set_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + IWL_ERR(priv, "Uncorrectable OTP ECC error, abort OTP read\n"); + return -EINVAL; + } + if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { + /* continue in this case */ + /* set the correctable OTP ECC bit for acknowledgement */ + iwl_set_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); + IWL_ERR(priv, "Correctable OTP ECC error, continue read\n"); + } + *eeprom_data = le16_to_cpu((__force __le16)(r >> 16)); + return 0; +} + +/* + * iwl_is_otp_empty: check for empty OTP + */ +static bool iwl_is_otp_empty(struct iwl_priv *priv) +{ + u16 next_link_addr = 0, link_value; + bool is_empty = false; + + /* locate the beginning of OTP link list */ + if (!iwl_read_otp_word(priv, next_link_addr, &link_value)) { + if (!link_value) { + IWL_ERR(priv, "OTP is empty\n"); + is_empty = true; + } + } else { + IWL_ERR(priv, "Unable to read first block of OTP list.\n"); + is_empty = true; + } + + return is_empty; +} + + +/* + * iwl_find_otp_image: find EEPROM image in OTP + * finding the OTP block that contains the EEPROM image. + * the last valid block on the link list (the block _before_ the last block) + * is the block we should read and used to configure the device. + * If all the available OTP blocks are full, the last block will be the block + * we should read and used to configure the device. + * only perform this operation if shadow RAM is disabled + */ +static int iwl_find_otp_image(struct iwl_priv *priv, + u16 *validblockaddr) +{ + u16 next_link_addr = 0, link_value = 0, valid_addr; + int ret = 0; + int usedblocks = 0; + + /* set addressing mode to absolute to traverse the link list */ + iwl_set_otp_access(priv, IWL_OTP_ACCESS_ABSOLUTE); + + /* checking for empty OTP or error */ + if (iwl_is_otp_empty(priv)) + return -EINVAL; + + /* + * start traverse link list + * until reach the max number of OTP blocks + * different devices have different number of OTP blocks + */ + do { + /* save current valid block address + * check for more block on the link list + */ + valid_addr = next_link_addr; + next_link_addr = link_value; + IWL_DEBUG_INFO(priv, "OTP blocks %d addr 0x%x\n", + usedblocks, next_link_addr); + if (iwl_read_otp_word(priv, next_link_addr, &link_value)) + return -EINVAL; + if (!link_value) { + /* + * reach the end of link list, + * set address point to the starting address + * of the image + */ + goto done; + } + /* more in the link list, continue */ + usedblocks++; + } while (usedblocks < priv->cfg->max_ll_items); + /* OTP full, use last block */ + IWL_DEBUG_INFO(priv, "OTP is full, use last block\n"); +done: + *validblockaddr = valid_addr; + /* skip first 2 bytes (link list pointer) */ + *validblockaddr += 2; + return ret; +} + /** * iwl_eeprom_init - read EEPROM contents * @@ -263,14 +469,14 @@ int iwl_eeprom_init(struct iwl_priv *priv) int sz; int ret; u16 addr; - u32 otpgp; + u16 validblockaddr = 0; + u16 cache_addr = 0; priv->nvm_device_type = iwlcore_get_nvm_type(priv); - + if (priv->nvm_device_type == -ENOENT) + return -ENOENT; /* allocate eeprom */ - if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - priv->cfg->eeprom_size = - OTP_BLOCK_SIZE * OTP_LOWER_BLOCKS_TOTAL; + IWL_DEBUG_INFO(priv, "NVM size = %d\n", priv->cfg->eeprom_size); sz = priv->cfg->eeprom_size; priv->eeprom = kzalloc(sz, GFP_KERNEL); if (!priv->eeprom) { @@ -298,46 +504,31 @@ int iwl_eeprom_init(struct iwl_priv *priv) if (ret) { IWL_ERR(priv, "Failed to initialize OTP access.\n"); ret = -ENOENT; - goto err; + goto done; } _iwl_write32(priv, CSR_EEPROM_GP, iwl_read32(priv, CSR_EEPROM_GP) & ~CSR_EEPROM_GP_IF_OWNER_MSK); - /* clear */ - _iwl_write32(priv, CSR_OTP_GP_REG, - iwl_read32(priv, CSR_OTP_GP_REG) | + + iwl_set_bit(priv, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - - for (addr = 0; addr < sz; addr += sizeof(u16)) { - u32 r; - - _iwl_write32(priv, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - - ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(priv, "Time out reading OTP[%d]\n", addr); + /* traversing the linked list if no shadow ram supported */ + if (!priv->cfg->shadow_ram_support) { + if (iwl_find_otp_image(priv, &validblockaddr)) { + ret = -ENOENT; goto done; } - r = _iwl_read_direct32(priv, CSR_EEPROM_REG); - /* check for ECC errors: */ - otpgp = iwl_read32(priv, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { - /* stop in this case */ - IWL_ERR(priv, "Uncorrectable OTP ECC error, Abort OTP read\n"); + } + for (addr = validblockaddr; addr < validblockaddr + sz; + addr += sizeof(u16)) { + u16 eeprom_data; + + ret = iwl_read_otp_word(priv, addr, &eeprom_data); + if (ret) goto done; - } - if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { - /* continue in this case */ - _iwl_write32(priv, CSR_OTP_GP_REG, - iwl_read32(priv, CSR_OTP_GP_REG) | - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); - IWL_ERR(priv, "Correctable OTP ECC error, continue read\n"); - } - e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16)); + e[cache_addr / 2] = eeprom_data; + cache_addr += sizeof(u16); } } else { /* eeprom is an array of 16bit values */ @@ -458,13 +649,13 @@ static void iwl_init_band_reference(const struct iwl_priv *priv, iwl_eeprom_query_addr(priv, offset); *eeprom_ch_index = iwl_eeprom_band_5; break; - case 6: /* 2.4GHz FAT channels */ + case 6: /* 2.4GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); *eeprom_ch_info = (struct iwl_eeprom_channel *) iwl_eeprom_query_addr(priv, offset); *eeprom_ch_index = iwl_eeprom_band_6; break; - case 7: /* 5 GHz FAT channels */ + case 7: /* 5 GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); *eeprom_ch_info = (struct iwl_eeprom_channel *) iwl_eeprom_query_addr(priv, offset); @@ -480,14 +671,14 @@ static void iwl_init_band_reference(const struct iwl_priv *priv, ? # x " " : "") /** - * iwl_set_fat_chan_info - Copy fat channel info into driver's priv. + * iwl_mod_ht40_chan_info - Copy ht40 channel info into driver's priv. * * Does not set up a command, or touch hardware. */ -static int iwl_set_fat_chan_info(struct iwl_priv *priv, +static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, enum ieee80211_band band, u16 channel, const struct iwl_eeprom_channel *eeprom_ch, - u8 fat_extension_channel) + u8 clear_ht40_extension_channel) { struct iwl_channel_info *ch_info; @@ -497,7 +688,7 @@ static int iwl_set_fat_chan_info(struct iwl_priv *priv, if (!is_channel_valid(ch_info)) return -1; - IWL_DEBUG_INFO(priv, "FAT Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" + IWL_DEBUG_INFO(priv, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" " Ad-Hoc %ssupported\n", ch_info->channel, is_channel_a_band(ch_info) ? @@ -513,17 +704,189 @@ static int iwl_set_fat_chan_info(struct iwl_priv *priv, && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); - ch_info->fat_eeprom = *eeprom_ch; - ch_info->fat_max_power_avg = eeprom_ch->max_power_avg; - ch_info->fat_curr_txpow = eeprom_ch->max_power_avg; - ch_info->fat_min_power = 0; - ch_info->fat_scan_power = eeprom_ch->max_power_avg; - ch_info->fat_flags = eeprom_ch->flags; - ch_info->fat_extension_channel = fat_extension_channel; + ch_info->ht40_eeprom = *eeprom_ch; + ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; + ch_info->ht40_curr_txpow = eeprom_ch->max_power_avg; + ch_info->ht40_min_power = 0; + ch_info->ht40_scan_power = eeprom_ch->max_power_avg; + ch_info->ht40_flags = eeprom_ch->flags; + ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; return 0; } +/** + * iwl_get_max_txpower_avg - get the highest tx power from all chains. + * find the highest tx power from all chains for the channel + */ +static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element) +{ + s8 max_txpower_avg = 0; /* (dBm) */ + + IWL_DEBUG_INFO(priv, "%d - " + "chain_a: %d dB chain_b: %d dB " + "chain_c: %d dB mimo2: %d dB mimo3: %d dB\n", + element, + enhanced_txpower[element].chain_a_max >> 1, + enhanced_txpower[element].chain_b_max >> 1, + enhanced_txpower[element].chain_c_max >> 1, + enhanced_txpower[element].mimo2_max >> 1, + enhanced_txpower[element].mimo3_max >> 1); + /* Take the highest tx power from any valid chains */ + if ((priv->cfg->valid_tx_ant & ANT_A) && + (enhanced_txpower[element].chain_a_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_a_max; + if ((priv->cfg->valid_tx_ant & ANT_B) && + (enhanced_txpower[element].chain_b_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_b_max; + if ((priv->cfg->valid_tx_ant & ANT_C) && + (enhanced_txpower[element].chain_c_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_c_max; + if (((priv->cfg->valid_tx_ant == ANT_AB) | + (priv->cfg->valid_tx_ant == ANT_BC) | + (priv->cfg->valid_tx_ant == ANT_AC)) && + (enhanced_txpower[element].mimo2_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo2_max; + if ((priv->cfg->valid_tx_ant == ANT_ABC) && + (enhanced_txpower[element].mimo3_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo3_max; + + /* max. tx power in EEPROM is in 1/2 dBm format + * convert from 1/2 dBm to dBm + */ + return max_txpower_avg >> 1; +} + +/** + * iwl_update_common_txpower: update channel tx power + * update tx power per band based on EEPROM enhanced tx power info. + */ +static s8 iwl_update_common_txpower(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int section, int element) +{ + struct iwl_channel_info *ch_info; + int ch; + bool is_ht40 = false; + s8 max_txpower_avg; /* (dBm) */ + + /* it is common section, contain all type (Legacy, HT and HT40) + * based on the element in the section to determine + * is it HT 40 or not + */ + if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX) + is_ht40 = true; + max_txpower_avg = + iwl_get_max_txpower_avg(priv, enhanced_txpower, element); + ch_info = priv->channel_info; + + for (ch = 0; ch < priv->channel_count; ch++) { + /* find matching band and update tx power if needed */ + if ((ch_info->band == enhinfo[section].band) && + (ch_info->max_power_avg < max_txpower_avg) && (!is_ht40)) { + /* Update regulatory-based run-time data */ + ch_info->max_power_avg = ch_info->curr_txpow = + max_txpower_avg; + ch_info->scan_power = max_txpower_avg; + } + if ((ch_info->band == enhinfo[section].band) && is_ht40 && + ch_info->ht40_max_power_avg && + (ch_info->ht40_max_power_avg < max_txpower_avg)) { + /* Update regulatory-based run-time data */ + ch_info->ht40_max_power_avg = max_txpower_avg; + ch_info->ht40_curr_txpow = max_txpower_avg; + ch_info->ht40_scan_power = max_txpower_avg; + } + ch_info++; + } + return max_txpower_avg; +} + +/** + * iwl_update_channel_txpower: update channel tx power + * update channel tx power based on EEPROM enhanced tx power info. + */ +static s8 iwl_update_channel_txpower(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int section, int element) +{ + struct iwl_channel_info *ch_info; + int ch; + u8 channel; + s8 max_txpower_avg; /* (dBm) */ + + channel = enhinfo[section].iwl_eeprom_section_channel[element]; + max_txpower_avg = + iwl_get_max_txpower_avg(priv, enhanced_txpower, element); + + ch_info = priv->channel_info; + for (ch = 0; ch < priv->channel_count; ch++) { + /* find matching channel and update tx power if needed */ + if (ch_info->channel == channel) { + if ((ch_info->max_power_avg < max_txpower_avg) && + (!enhinfo[section].is_ht40)) { + /* Update regulatory-based run-time data */ + ch_info->max_power_avg = max_txpower_avg; + ch_info->curr_txpow = max_txpower_avg; + ch_info->scan_power = max_txpower_avg; + } + if ((enhinfo[section].is_ht40) && + (ch_info->ht40_max_power_avg) && + (ch_info->ht40_max_power_avg < max_txpower_avg)) { + /* Update regulatory-based run-time data */ + ch_info->ht40_max_power_avg = max_txpower_avg; + ch_info->ht40_curr_txpow = max_txpower_avg; + ch_info->ht40_scan_power = max_txpower_avg; + } + break; + } + ch_info++; + } + return max_txpower_avg; +} + +/** + * iwlcore_eeprom_enhanced_txpower: process enhanced tx power info + */ +void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv) +{ + int eeprom_section_count = 0; + int section, element; + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower; + u32 offset; + s8 max_txpower_avg; /* (dBm) */ + + /* Loop through all the sections + * adjust bands and channel's max tx power + * Set the tx_power_user_lmt to the highest power + * supported by any channels and chains + */ + for (section = 0; section < ARRAY_SIZE(enhinfo); section++) { + eeprom_section_count = enhinfo[section].count; + offset = enhinfo[section].offset; + enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *) + iwl_eeprom_query_addr(priv, offset); + + for (element = 0; element < eeprom_section_count; element++) { + if (enhinfo[section].is_common) + max_txpower_avg = + iwl_update_common_txpower(priv, + enhanced_txpower, section, element); + else + max_txpower_avg = + iwl_update_channel_txpower(priv, + enhanced_txpower, section, element); + + /* Update the tx_power_user_lmt to the highest power + * supported by any channel */ + if (max_txpower_avg > priv->tx_power_user_lmt) + priv->tx_power_user_lmt = max_txpower_avg; + } + } +} +EXPORT_SYMBOL(iwlcore_eeprom_enhanced_txpower); + #define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ ? # x " " : "") @@ -585,11 +948,10 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* Copy the run-time flags so they are there even on * invalid channels */ ch_info->flags = eeprom_ch_info[ch].flags; - /* First write that fat is not enabled, and then enable + /* First write that ht40 is not enabled, and then enable * one by one */ - ch_info->fat_extension_channel = - (IEEE80211_CHAN_NO_HT40PLUS | - IEEE80211_CHAN_NO_HT40MINUS); + ch_info->ht40_extension_channel = + IEEE80211_CHAN_NO_HT40; if (!(is_channel_valid(ch_info))) { IWL_DEBUG_INFO(priv, "Ch. %d Flags %x [%sGHz] - " @@ -638,17 +1000,16 @@ int iwl_init_channel_map(struct iwl_priv *priv) } } - /* Check if we do have FAT channels */ + /* Check if we do have HT40 channels */ if (priv->cfg->ops->lib->eeprom_ops.regulatory_bands[5] == - EEPROM_REGULATORY_BAND_NO_FAT && + EEPROM_REGULATORY_BAND_NO_HT40 && priv->cfg->ops->lib->eeprom_ops.regulatory_bands[6] == - EEPROM_REGULATORY_BAND_NO_FAT) + EEPROM_REGULATORY_BAND_NO_HT40) return 0; - /* Two additional EEPROM bands for 2.4 and 5 GHz FAT channels */ + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ for (band = 6; band <= 7; band++) { enum ieee80211_band ieeeband; - u8 fat_extension_chan; iwl_init_band_reference(priv, band, &eeprom_ch_count, &eeprom_ch_info, &eeprom_ch_index); @@ -659,31 +1020,28 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* Loop through each band adding each of the channels */ for (ch = 0; ch < eeprom_ch_count; ch++) { - - if ((band == 6) && - ((eeprom_ch_index[ch] == 5) || - (eeprom_ch_index[ch] == 6) || - (eeprom_ch_index[ch] == 7))) - /* both are allowed: above and below */ - fat_extension_chan = 0; - else - fat_extension_chan = - IEEE80211_CHAN_NO_HT40MINUS; - /* Set up driver's info for lower half */ - iwl_set_fat_chan_info(priv, ieeeband, + iwl_mod_ht40_chan_info(priv, ieeeband, eeprom_ch_index[ch], - &(eeprom_ch_info[ch]), - fat_extension_chan); + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40PLUS); /* Set up driver's info for upper half */ - iwl_set_fat_chan_info(priv, ieeeband, - (eeprom_ch_index[ch] + 4), - &(eeprom_ch_info[ch]), - IEEE80211_CHAN_NO_HT40PLUS); + iwl_mod_ht40_chan_info(priv, ieeeband, + eeprom_ch_index[ch] + 4, + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40MINUS); } } + /* for newer device (6000 series and up) + * EEPROM contain enhanced tx power information + * driver need to process addition information + * to determine the max channel tx power limits + */ + if (priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower) + priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower(priv); + return 0; } EXPORT_SYMBOL(iwl_init_channel_map); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 195b4ef12c2..6b68db7b1b8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -88,10 +88,10 @@ struct iwl_priv; * requirement for establishing a new network for legal operation on channels * requiring RADAR detection or restricting ACTIVE scanning. * - * NOTE: "WIDE" flag does not indicate anything about "FAT" 40 MHz channels. - * It only indicates that 20 MHz channel use is supported; FAT channel + * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. + * It only indicates that 20 MHz channel use is supported; HT40 channel * usage is indicated by a separate set of regulatory flags for each - * FAT channel pair. + * HT40 channel pair. * * NOTE: Using a channel inappropriately will result in a uCode error! */ @@ -112,12 +112,36 @@ enum { #define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) /* *regulatory* channel data format in eeprom, one for each channel. - * There are separate entries for FAT (40 MHz) vs. normal (20 MHz) channels. */ + * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ struct iwl_eeprom_channel { u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ } __attribute__ ((packed)); +/** + * iwl_eeprom_enhanced_txpwr structure + * This structure presents the enhanced regulatory tx power limit layout + * in eeprom image + * Enhanced regulatory tx power portion of eeprom image can be broken down + * into individual structures; each one is 8 bytes in size and contain the + * following information + * @chain_a_max_pwr: chain a max power in 1/2 dBm + * @chain_b_max_pwr: chain b max power in 1/2 dBm + * @chain_c_max_pwr: chain c max power in 1/2 dBm + * @mimo2_max_pwr: mimo2 max power in 1/2 dBm + * @mimo3_max_pwr: mimo3 max power in 1/2 dBm + * + */ +struct iwl_eeprom_enhanced_txpwr { + u16 reserved; + s8 chain_a_max; + s8 chain_b_max; + s8 chain_c_max; + s8 reserved1; + s8 mimo2_max; + s8 mimo3_max; +} __attribute__ ((packed)); + /* 3945 Specific */ #define EEPROM_3945_EEPROM_VERSION (0x2f) @@ -170,18 +194,77 @@ struct iwl_eeprom_channel { | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ #define EEPROM_5000_REG_BAND_5_CHANNELS ((0x74)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 12 bytes */ -#define EEPROM_5000_REG_BAND_24_FAT_CHANNELS ((0x82)\ +#define EEPROM_5000_REG_BAND_24_HT40_CHANNELS ((0x82)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ -#define EEPROM_5000_REG_BAND_52_FAT_CHANNELS ((0x92)\ +#define EEPROM_5000_REG_BAND_52_HT40_CHANNELS ((0x92)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ +/* 6000 and up regulatory tx power - indirect access */ +/* max. elements per section */ +#define EEPROM_MAX_TXPOWER_SECTION_ELEMENTS (8) +#define EEPROM_TXPOWER_COMMON_HT40_INDEX (2) + +/** + * Partition the enhanced tx power portion of eeprom image into + * 10 sections based on band, modulation, frequency and channel + * + * Section 1: all CCK channels + * Section 2: all 2.4 GHz OFDM (Legacy, HT and HT40 ) channels + * Section 3: all 5.2 GHz OFDM (Legacy, HT and HT40) channels + * Section 4: 2.4 GHz 20MHz channels: 1, 2, 10, 11. Both Legacy and HT + * Section 5: 2.4 GHz 40MHz channels: 1, 2, 6, 7, 9, (_above_) + * Section 6: 5.2 GHz 20MHz channels: 36, 64, 100, both Legacy and HT + * Section 7: 5.2 GHz 40MHz channels: 36, 60, 100 (_above_) + * Section 8: 2.4 GHz channel 13, Both Legacy and HT + * Section 9: 2.4 GHz channel 140, Both Legacy and HT + * Section 10: 2.4 GHz 40MHz channels: 132, 44 (_above_) + */ +/* 2.4 GHz band: CCK */ +#define EEPROM_LB_CCK_20_COMMON ((0xAA)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 8 bytes */ +/* 2.4 GHz band: 20MHz-Legacy, 20MHz-HT, 40MHz-HT */ +#define EEPROM_LB_OFDM_COMMON ((0xB2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +/* 5.2 GHz band: 20MHz-Legacy, 20MHz-HT, 40MHz-HT */ +#define EEPROM_HB_OFDM_COMMON ((0xCA)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +/* 2.4GHz band channels: + * 1Legacy, 1HT, 2Legacy, 2HT, 10Legacy, 10HT, 11Legacy, 11HT */ +#define EEPROM_LB_OFDM_20_BAND ((0xE2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 64 bytes */ +/* 2.4 GHz band HT40 channels: (1,+1) (2,+1) (6,+1) (7,+1) (9,+1) */ +#define EEPROM_LB_OFDM_HT40_BAND ((0x122)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 40 bytes */ +/* 5.2GHz band channels: 36Legacy, 36HT, 64Legacy, 64HT, 100Legacy, 100HT */ +#define EEPROM_HB_OFDM_20_BAND ((0x14A)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 48 bytes */ +/* 5.2 GHz band HT40 channels: (36,+1) (60,+1) (100,+1) */ +#define EEPROM_HB_OFDM_HT40_BAND ((0x17A)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +/* 2.4 GHz band, channnel 13: Legacy, HT */ +#define EEPROM_LB_OFDM_20_CHANNEL_13 ((0x192)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ +/* 5.2 GHz band, channnel 140: Legacy, HT */ +#define EEPROM_HB_OFDM_20_CHANNEL_140 ((0x1A2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ +/* 5.2 GHz band, HT40 channnels (132,+1) (44,+1) */ +#define EEPROM_HB_OFDM_HT40_BAND_1 ((0x1B2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ + + /* 5050 Specific */ #define EEPROM_5050_TX_POWER_VERSION (4) #define EEPROM_5050_EEPROM_VERSION (0x21E) /* OTP */ -#define OTP_LOWER_BLOCKS_TOTAL (3) -#define OTP_BLOCK_SIZE (0x400) +/* lower blocks contain EEPROM image and calibration data */ +#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ +/* high blocks contain PAPD data */ +#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ +#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ +#define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */ +#define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */ +#define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */ /* 2.4 GHz */ extern const u8 iwl_eeprom_band_1[14]; @@ -313,7 +396,7 @@ struct iwl_eeprom_calib_info { * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * - * Entries immediately below are for 20 MHz channel width. FAT (40 MHz) + * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) * channels (only for 4965, not supported by 3945) appear later in the EEPROM. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -352,29 +435,29 @@ struct iwl_eeprom_calib_info { #define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */ /* - * 2.4 GHz FAT channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) + * 2.4 GHz HT40 channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) * * The channel listed is the center of the lower 20 MHz half of the channel. * The overall center frequency is actually 2 channels (10 MHz) above that, - * and the upper half of each FAT channel is centered 4 channels (20 MHz) away - * from the lower half; e.g. the upper half of FAT channel 1 is channel 5, - * and the overall FAT channel width centers on channel 3. + * and the upper half of each HT40 channel is centered 4 channels (20 MHz) away + * from the lower half; e.g. the upper half of HT40 channel 1 is channel 5, + * and the overall HT40 channel width centers on channel 3. * * NOTE: The RXON command uses 20 MHz channel numbers to specify the * control channel to which to tune. RXON also specifies whether the - * control channel is the upper or lower half of a FAT channel. + * control channel is the upper or lower half of a HT40 channel. * - * NOTE: 4965 does not support FAT channels on 2.4 GHz. + * NOTE: 4965 does not support HT40 channels on 2.4 GHz. */ -#define EEPROM_4965_REGULATORY_BAND_24_FAT_CHANNELS (2*0xA0) /* 14 bytes */ +#define EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS (2*0xA0) /* 14 bytes */ /* - * 5.2 GHz FAT channels 36 (40), 44 (48), 52 (56), 60 (64), + * 5.2 GHz HT40 channels 36 (40), 44 (48), 52 (56), 60 (64), * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) */ -#define EEPROM_4965_REGULATORY_BAND_52_FAT_CHANNELS (2*0xA8) /* 22 bytes */ +#define EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS (2*0xA8) /* 22 bytes */ -#define EEPROM_REGULATORY_BAND_NO_FAT (0) +#define EEPROM_REGULATORY_BAND_NO_HT40 (0) struct iwl_eeprom_ops { const u32 regulatory_bands[7]; @@ -383,6 +466,7 @@ struct iwl_eeprom_ops { void (*release_semaphore) (struct iwl_priv *priv); u16 (*calib_version) (struct iwl_priv *priv); const u8* (*query_addr) (const struct iwl_priv *priv, size_t offset); + void (*update_enhanced_txpower) (struct iwl_priv *priv); }; @@ -397,7 +481,7 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv); int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv); void iwlcore_eeprom_release_semaphore(struct iwl_priv *priv); const u8 *iwlcore_eeprom_query_addr(const struct iwl_priv *priv, size_t offset); - +void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv); int iwl_init_channel_map(struct iwl_priv *priv); void iwl_free_channel_map(struct iwl_priv *priv); const struct iwl_channel_info *iwl_get_channel_info( diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 17d61ac8ed6..532c8d6cd8d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -36,8 +36,6 @@ #include "iwl-core.h" -#define IWL_CMD(x) case x: return #x - const char *get_cmd_string(u8 cmd) { switch (cmd) { @@ -103,22 +101,23 @@ EXPORT_SYMBOL(get_cmd_string); #define HOST_COMPLETE_TIMEOUT (HZ / 2) -static int iwl_generic_cmd_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb) +static void iwl_generic_cmd_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { struct iwl_rx_packet *pkt = NULL; if (!skb) { IWL_ERR(priv, "Error: Response NULL in %s.\n", get_cmd_string(cmd->hdr.cmd)); - return 1; + return; } pkt = (struct iwl_rx_packet *)skb->data; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - return 1; + return; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -127,29 +126,26 @@ static int iwl_generic_cmd_callback(struct iwl_priv *priv, case SENSITIVITY_CMD: IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - break; + break; default: IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); } #endif - - /* Let iwl_tx_complete free the response skb */ - return 1; } static int iwl_send_cmd_async(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { int ret; - BUG_ON(!(cmd->meta.flags & CMD_ASYNC)); + BUG_ON(!(cmd->flags & CMD_ASYNC)); /* An asynchronous command can not expect an SKB to be set. */ - BUG_ON(cmd->meta.flags & CMD_WANT_SKB); + BUG_ON(cmd->flags & CMD_WANT_SKB); /* Assign a generic callback if one is not provided */ - if (!cmd->meta.u.callback) - cmd->meta.u.callback = iwl_generic_cmd_callback; + if (!cmd->callback) + cmd->callback = iwl_generic_cmd_callback; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return -EBUSY; @@ -168,10 +164,10 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) int cmd_idx; int ret; - BUG_ON(cmd->meta.flags & CMD_ASYNC); + BUG_ON(cmd->flags & CMD_ASYNC); /* A synchronous command can not have a callback set. */ - BUG_ON(cmd->meta.u.callback != NULL); + BUG_ON(cmd->callback); if (test_and_set_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status)) { IWL_ERR(priv, @@ -183,9 +179,6 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) set_bit(STATUS_HCMD_ACTIVE, &priv->status); - if (cmd->meta.flags & CMD_WANT_SKB) - cmd->meta.source = &cmd->meta; - cmd_idx = iwl_enqueue_hcmd(priv, cmd); if (cmd_idx < 0) { ret = cmd_idx; @@ -222,7 +215,7 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) ret = -EIO; goto fail; } - if ((cmd->meta.flags & CMD_WANT_SKB) && !cmd->meta.u.skb) { + if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_skb) { IWL_ERR(priv, "Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); ret = -EIO; @@ -233,20 +226,20 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) goto out; cancel: - if (cmd->meta.flags & CMD_WANT_SKB) { - struct iwl_cmd *qcmd; - - /* Cancel the CMD_WANT_SKB flag for the cmd in the + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the * TX cmd queue. Otherwise in case the cmd comes * in later, it will possibly set an invalid - * address (cmd->meta.source). */ - qcmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_idx]; - qcmd->meta.flags &= ~CMD_WANT_SKB; + * address (cmd->meta.source). + */ + priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_idx].flags &= + ~CMD_WANT_SKB; } fail: - if (cmd->meta.u.skb) { - dev_kfree_skb_any(cmd->meta.u.skb); - cmd->meta.u.skb = NULL; + if (cmd->reply_skb) { + dev_kfree_skb_any(cmd->reply_skb); + cmd->reply_skb = NULL; } out: clear_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status); @@ -256,7 +249,7 @@ EXPORT_SYMBOL(iwl_send_cmd_sync); int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { - if (cmd->meta.flags & CMD_ASYNC) + if (cmd->flags & CMD_ASYNC) return iwl_send_cmd_async(priv, cmd); return iwl_send_cmd_sync(priv, cmd); @@ -277,9 +270,9 @@ EXPORT_SYMBOL(iwl_send_cmd_pdu); int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, - int (*callback)(struct iwl_priv *priv, - struct iwl_cmd *cmd, - struct sk_buff *skb)) + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb)) { struct iwl_host_cmd cmd = { .id = id, @@ -287,8 +280,8 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, .data = data, }; - cmd.meta.flags |= CMD_ASYNC; - cmd.meta.u.callback = callback; + cmd.flags |= CMD_ASYNC; + cmd.callback = callback; return iwl_send_cmd_async(priv, &cmd); } diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index a1328c3c81a..bd0b12efb5c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -145,4 +145,25 @@ static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue) #define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue #define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue +static inline void iwl_disable_interrupts(struct iwl_priv *priv) +{ + clear_bit(STATUS_INT_ENABLED, &priv->status); + + /* disable interrupts from uCode/NIC to host */ + iwl_write32(priv, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl_write32(priv, CSR_INT, 0xffffffff); + iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff); + IWL_DEBUG_ISR(priv, "Disabled interrupts\n"); +} + +static inline void iwl_enable_interrupts(struct iwl_priv *priv) +{ + IWL_DEBUG_ISR(priv, "Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &priv->status); + iwl_write32(priv, CSR_INT_MASK, priv->inta_mask); +} + #endif /* __iwl_helpers_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 5e64252f80f..f420c99e724 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -54,7 +54,7 @@ static const char *led_type_str[] = { static const struct { - u16 tpt; + u16 tpt; /* Mb/s */ u8 on_time; u8 off_time; } blink_tbl[] = @@ -91,8 +91,8 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) .id = REPLY_LEDS_CMD, .len = sizeof(struct iwl_led_cmd), .data = led_cmd, - .meta.flags = CMD_ASYNC, - .meta.u.callback = NULL, + .flags = CMD_ASYNC, + .callback = NULL, }; u32 reg; @@ -104,7 +104,7 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) } /* Set led pattern command */ -static int iwl4965_led_pattern(struct iwl_priv *priv, int led_id, +static int iwl_led_pattern(struct iwl_priv *priv, int led_id, unsigned int idx) { struct iwl_led_cmd led_cmd = { @@ -121,7 +121,7 @@ static int iwl4965_led_pattern(struct iwl_priv *priv, int led_id, } /* Set led register off */ -static int iwl4965_led_on_reg(struct iwl_priv *priv, int led_id) +static int iwl_led_on_reg(struct iwl_priv *priv, int led_id) { IWL_DEBUG_LED(priv, "led on %d\n", led_id); iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON); @@ -130,7 +130,7 @@ static int iwl4965_led_on_reg(struct iwl_priv *priv, int led_id) #if 0 /* Set led on command */ -static int iwl4965_led_on(struct iwl_priv *priv, int led_id) +static int iwl_led_on(struct iwl_priv *priv, int led_id) { struct iwl_led_cmd led_cmd = { .id = led_id, @@ -142,7 +142,7 @@ static int iwl4965_led_on(struct iwl_priv *priv, int led_id) } /* Set led off command */ -int iwl4965_led_off(struct iwl_priv *priv, int led_id) +int iwl_led_off(struct iwl_priv *priv, int led_id) { struct iwl_led_cmd led_cmd = { .id = led_id, @@ -157,7 +157,7 @@ int iwl4965_led_off(struct iwl_priv *priv, int led_id) /* Set led register off */ -static int iwl4965_led_off_reg(struct iwl_priv *priv, int led_id) +static int iwl_led_off_reg(struct iwl_priv *priv, int led_id) { IWL_DEBUG_LED(priv, "LED Reg off\n"); iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF); @@ -171,7 +171,7 @@ static int iwl_led_associate(struct iwl_priv *priv, int led_id) { IWL_DEBUG_LED(priv, "Associated\n"); priv->allow_blinking = 1; - return iwl4965_led_on_reg(priv, led_id); + return iwl_led_on_reg(priv, led_id); } static int iwl_led_disassociate(struct iwl_priv *priv, int led_id) { @@ -264,13 +264,16 @@ static int iwl_leds_register_led(struct iwl_priv *priv, struct iwl_led *led, /* - * calculate blink rate according to last 2 sec Tx/Rx activities + * calculate blink rate according to last second Tx/Rx activities */ static int iwl_get_blink_rate(struct iwl_priv *priv) { int i; - u64 current_tpt = priv->tx_stats[2].bytes; - /* FIXME: + priv->rx_stats[2].bytes; */ + /* count both tx and rx traffic to be able to + * handle traffic in either direction + */ + u64 current_tpt = priv->tx_stats.data_bytes + + priv->rx_stats.data_bytes; s64 tpt = current_tpt - priv->led_tpt; if (tpt < 0) /* wraparound */ @@ -314,7 +317,7 @@ void iwl_leds_background(struct iwl_priv *priv) priv->last_blink_time = 0; if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) { priv->last_blink_rate = IWL_SOLID_BLINK_IDX; - iwl4965_led_pattern(priv, IWL_LED_LINK, + iwl_led_pattern(priv, IWL_LED_LINK, IWL_SOLID_BLINK_IDX); } return; @@ -328,12 +331,11 @@ void iwl_leds_background(struct iwl_priv *priv) /* call only if blink rate change */ if (blink_idx != priv->last_blink_rate) - iwl4965_led_pattern(priv, IWL_LED_LINK, blink_idx); + iwl_led_pattern(priv, IWL_LED_LINK, blink_idx); priv->last_blink_time = jiffies; priv->last_blink_rate = blink_idx; } -EXPORT_SYMBOL(iwl_leds_background); /* Register all led handler */ int iwl_leds_register(struct iwl_priv *priv) @@ -351,8 +353,8 @@ int iwl_leds_register(struct iwl_priv *priv) sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio", wiphy_name(priv->hw->wiphy)); - priv->led[IWL_LED_TRG_RADIO].led_on = iwl4965_led_on_reg; - priv->led[IWL_LED_TRG_RADIO].led_off = iwl4965_led_off_reg; + priv->led[IWL_LED_TRG_RADIO].led_on = iwl_led_on_reg; + priv->led[IWL_LED_TRG_RADIO].led_off = iwl_led_off_reg; priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL; ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RADIO], @@ -386,7 +388,7 @@ int iwl_leds_register(struct iwl_priv *priv) priv->led[IWL_LED_TRG_RX].led_on = iwl_led_associated; priv->led[IWL_LED_TRG_RX].led_off = iwl_led_associated; - priv->led[IWL_LED_TRG_RX].led_pattern = iwl4965_led_pattern; + priv->led[IWL_LED_TRG_RX].led_pattern = iwl_led_pattern; if (ret) goto exit_fail; @@ -401,7 +403,7 @@ int iwl_leds_register(struct iwl_priv *priv) priv->led[IWL_LED_TRG_TX].led_on = iwl_led_associated; priv->led[IWL_LED_TRG_TX].led_off = iwl_led_associated; - priv->led[IWL_LED_TRG_TX].led_pattern = iwl4965_led_pattern; + priv->led[IWL_LED_TRG_TX].led_pattern = iwl_led_pattern; if (ret) goto exit_fail; diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index f2ea3f05f6e..0b16841f45f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -36,25 +36,41 @@ #include "iwl-eeprom.h" #include "iwl-dev.h" #include "iwl-core.h" +#include "iwl-io.h" #include "iwl-commands.h" #include "iwl-debug.h" #include "iwl-power.h" /* - * Setting power level allow the card to go to sleep when not busy. + * Setting power level allows the card to go to sleep when not busy. * - * The power level is set to INDEX_1 (the least deep state) by - * default, and will, in the future, be the deepest state unless - * otherwise required by pm_qos network latency requirements. - * - * Using INDEX_1 without pm_qos is ok because mac80211 will disable - * PS when even checking every beacon for the TIM bit would exceed - * the required latency. + * We calculate a sleep command based on the required latency, which + * we get from mac80211. In order to handle thermal throttling, we can + * also use pre-defined power levels. */ -#define IWL_POWER_RANGE_0_MAX (2) -#define IWL_POWER_RANGE_1_MAX (10) +/* + * For now, keep using power level 1 instead of automatically + * adjusting ... + */ +bool no_sleep_autoadjust = true; +module_param(no_sleep_autoadjust, bool, S_IRUGO); +MODULE_PARM_DESC(no_sleep_autoadjust, + "don't automatically adjust sleep level " + "according to maximum network latency"); +/* + * This defines the old power levels. They are still used by default + * (level 1) and for thermal throttle (levels 3 through 5) + */ + +struct iwl_power_vec_entry { + struct iwl_powertable_cmd cmd; + u8 no_dtim; +}; + +#define IWL_DTIM_RANGE_0_MAX 2 +#define IWL_DTIM_RANGE_1_MAX 10 #define NOSLP cpu_to_le16(0), 0, 0 #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 @@ -66,9 +82,8 @@ cpu_to_le32(X3), \ cpu_to_le32(X4)} /* default power management (not Tx power) table values */ -/* for DTIM period 0 through IWL_POWER_RANGE_0_MAX */ +/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { - {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, @@ -77,9 +92,8 @@ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { }; -/* for DTIM period IWL_POWER_RANGE_0_MAX + 1 through IWL_POWER_RANGE_1_MAX */ +/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { - {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, @@ -87,9 +101,8 @@ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2} }; -/* for DTIM period > IWL_POWER_RANGE_1_MAX */ +/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { - {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, @@ -97,80 +110,29 @@ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} }; - -/* set card power command */ -static int iwl_set_power(struct iwl_priv *priv, void *cmd) -{ - return iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, - sizeof(struct iwl_powertable_cmd), cmd); -} - -/* initialize to default */ -static void iwl_power_init_handle(struct iwl_priv *priv) -{ - struct iwl_power_mgr *pow_data; - int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_NUM; - struct iwl_powertable_cmd *cmd; - int i; - u16 lctl; - - IWL_DEBUG_POWER(priv, "Initialize power \n"); - - pow_data = &priv->power_data; - - memset(pow_data, 0, sizeof(*pow_data)); - - memcpy(&pow_data->pwr_range_0[0], &range_0[0], size); - memcpy(&pow_data->pwr_range_1[0], &range_1[0], size); - memcpy(&pow_data->pwr_range_2[0], &range_2[0], size); - - lctl = iwl_pcie_link_ctl(priv); - - IWL_DEBUG_POWER(priv, "adjust power command flags\n"); - - for (i = 0; i < IWL_POWER_NUM; i++) { - cmd = &pow_data->pwr_range_0[i].cmd; - - if (lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN) - cmd->flags &= ~IWL_POWER_PCI_PM_MSK; - else - cmd->flags |= IWL_POWER_PCI_PM_MSK; - } -} - -/* adjust power command according to DTIM period and power level*/ -static int iwl_update_power_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd, u16 mode) +static void iwl_static_sleep_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd, + enum iwl_power_level lvl, int period) { - struct iwl_power_vec_entry *range; - struct iwl_power_mgr *pow_data; - int i; - u32 max_sleep = 0; - u8 period; + const struct iwl_power_vec_entry *table; + int max_sleep, i; bool skip; - if (mode > IWL_POWER_INDEX_5) { - IWL_DEBUG_POWER(priv, "Error invalid power mode \n"); - return -EINVAL; - } + table = range_2; + if (period < IWL_DTIM_RANGE_1_MAX) + table = range_1; + if (period < IWL_DTIM_RANGE_0_MAX) + table = range_0; - pow_data = &priv->power_data; + BUG_ON(lvl < 0 || lvl >= IWL_POWER_NUM); - if (pow_data->dtim_period <= IWL_POWER_RANGE_0_MAX) - range = &pow_data->pwr_range_0[0]; - else if (pow_data->dtim_period <= IWL_POWER_RANGE_1_MAX) - range = &pow_data->pwr_range_1[0]; - else - range = &pow_data->pwr_range_2[0]; - - period = pow_data->dtim_period; - memcpy(cmd, &range[mode].cmd, sizeof(struct iwl_powertable_cmd)); + *cmd = table[lvl].cmd; if (period == 0) { - period = 1; skip = false; + period = 1; } else { - skip = !!range[mode].no_dtim; + skip = !!table[lvl].no_dtim; } if (skip) { @@ -178,7 +140,7 @@ static int iwl_update_power_cmd(struct iwl_priv *priv, max_sleep = le32_to_cpu(slp_itrvl); if (max_sleep == 0xFF) max_sleep = period * (skip + 1); - else if (max_sleep > period) + else if (max_sleep > period) max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; } else { @@ -190,6 +152,92 @@ static int iwl_update_power_cmd(struct iwl_priv *priv, if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) cmd->sleep_interval[i] = cpu_to_le32(max_sleep); + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + else + cmd->flags &= ~IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); +} + +/* default Thermal Throttling transaction table + * Current state | Throttling Down | Throttling Up + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, + {IWL_TI_1, 105, CT_KILL_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, + {IWL_TI_2, 110, CT_KILL_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; + +/* Advance Thermal Throttling default restriction table */ +static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { + {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, + {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } +}; + + +static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); +} + +static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd, + int dynps_ms, int wakeup_period) +{ + int i; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->flags = IWL_POWER_DRIVER_ALLOW_SLEEP_MSK | + IWL_POWER_FAST_PD; /* no use seeing frames for others */ + + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms); + cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms); + + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + cmd->sleep_interval[i] = cpu_to_le32(wakeup_period); + + IWL_DEBUG_POWER(priv, "Automatic sleep command\n"); +} + +static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) +{ + IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); @@ -200,79 +248,587 @@ static int iwl_update_power_cmd(struct iwl_priv *priv, le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); - return 0; + return iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, + sizeof(struct iwl_powertable_cmd), cmd); } -/* - * compute the final power mode index - */ int iwl_power_update_mode(struct iwl_priv *priv, bool force) { - struct iwl_power_mgr *setting = &(priv->power_data); int ret = 0; - u16 uninitialized_var(final_mode); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + bool enabled = (priv->iw_mode == NL80211_IFTYPE_STATION) && + (priv->hw->conf.flags & IEEE80211_CONF_PS); bool update_chains; + struct iwl_powertable_cmd cmd; + int dtimper; /* Don't update the RX chain when chain noise calibration is running */ update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; - final_mode = priv->power_data.user_power_setting; - - if (setting->power_disabled) - final_mode = IWL_POWER_MODE_CAM; + if (priv->vif) + dtimper = priv->vif->bss_conf.dtim_period; + else + dtimper = 1; + + /* TT power setting overwrites everything */ + if (tt->state >= IWL_TI_1) + iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper); + else if (!enabled) + iwl_power_sleep_cam_cmd(priv, &cmd); + else if (priv->power_data.debug_sleep_level_override >= 0) + iwl_static_sleep_cmd(priv, &cmd, + priv->power_data.debug_sleep_level_override, + dtimper); + else if (no_sleep_autoadjust) + iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_1, dtimper); + else + iwl_power_fill_sleep_cmd(priv, &cmd, + priv->hw->conf.dynamic_ps_timeout, + priv->hw->conf.max_sleep_period); if (iwl_is_ready_rf(priv) && - ((setting->power_mode != final_mode) || force)) { - struct iwl_powertable_cmd cmd; - - if (final_mode != IWL_POWER_MODE_CAM) + (memcmp(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)) || force)) { + if (cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) set_bit(STATUS_POWER_PMI, &priv->status); - iwl_update_power_cmd(priv, &cmd, final_mode); - cmd.keep_alive_beacons = 0; - - if (final_mode == IWL_POWER_INDEX_5) - cmd.flags |= IWL_POWER_FAST_PD; - ret = iwl_set_power(priv, &cmd); - - if (final_mode == IWL_POWER_MODE_CAM) - clear_bit(STATUS_POWER_PMI, &priv->status); - - if (priv->cfg->ops->lib->update_chain_flags && update_chains) - priv->cfg->ops->lib->update_chain_flags(priv); - else - IWL_DEBUG_POWER(priv, "Cannot update the power, chain noise " + if (!ret) { + if (!(cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) + clear_bit(STATUS_POWER_PMI, &priv->status); + + if (priv->cfg->ops->lib->update_chain_flags && + update_chains) + priv->cfg->ops->lib->update_chain_flags(priv); + else + IWL_DEBUG_POWER(priv, + "Cannot update the power, chain noise " "calibration running: %d\n", priv->chain_noise_data.state); - if (!ret) - setting->power_mode = final_mode; + memcpy(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)); + } else + IWL_ERR(priv, "set power fail, ret = %d", ret); } return ret; } EXPORT_SYMBOL(iwl_power_update_mode); -/* set user_power_setting */ -int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode) +bool iwl_ht_enabled(struct iwl_priv *priv) { - if (mode >= IWL_POWER_NUM) - return -EINVAL; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; - priv->power_data.user_power_setting = mode; + if (!priv->thermal_throttle.advanced_tt) + return true; + restriction = tt->restriction + tt->state; + return restriction->is_ht; +} +EXPORT_SYMBOL(iwl_ht_enabled); + +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; - return iwl_power_update_mode(priv, 0); + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->tx_stream; } -EXPORT_SYMBOL(iwl_power_set_user_mode); +EXPORT_SYMBOL(iwl_tx_ant_restriction); + +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->rx_stream; +} + +#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ + +/* + * toggle the bit to wake up uCode and check the temperature + * if the temperature is below CT, uCode will stay awake and send card + * state notification with CT_KILL bit clear to inform Thermal Throttling + * Management to change state. Otherwise, uCode will go back to sleep + * without doing anything, driver should continue the 5 seconds timer + * to wake up uCode for temperature check until temperature drop below CT + */ +static void iwl_tt_check_exit_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + unsigned long flags; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (tt->state == IWL_TI_CT_KILL) { + if (priv->thermal_throttle.ct_kill_toggle) { + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = false; + } else { + iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = true; + } + iwl_read32(priv, CSR_UCODE_DRV_GP1); + spin_lock_irqsave(&priv->reg_lock, flags); + if (!iwl_grab_nic_access(priv)) + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, flags); + + /* Reschedule the ct_kill timer to occur in + * CT_KILL_EXIT_DURATION seconds to ensure we get a + * thermal update */ + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + + CT_KILL_EXIT_DURATION * HZ); + } +} + +static void iwl_perform_ct_kill_task(struct iwl_priv *priv, + bool stop) +{ + if (stop) { + IWL_DEBUG_POWER(priv, "Stop all queues\n"); + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + IWL_DEBUG_POWER(priv, + "Schedule 5 seconds CT_KILL Timer\n"); + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + + CT_KILL_EXIT_DURATION * HZ); + } else { + IWL_DEBUG_POWER(priv, "Wake all queues\n"); + if (priv->mac80211_registered) + ieee80211_wake_queues(priv->hw); + } +} + +#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) + +/* + * Legacy thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + */ +static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + enum iwl_tt_state old_state; + +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_POWER(priv, + "Temperature increase %d degree Celsius\n", + (temp - tt->tt_previous_temp)); + } +#endif + old_state = tt->state; + /* in Celsius */ + if (temp >= IWL_MINIMAL_POWER_THRESHOLD) + tt->state = IWL_TI_CT_KILL; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) + tt->state = IWL_TI_2; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) + tt->state = IWL_TI_1; + else + tt->state = IWL_TI_0; + +#ifdef CONFIG_IWLWIFI_DEBUG + tt->tt_previous_temp = temp; +#endif + if (tt->state != old_state) { + switch (tt->state) { + case IWL_TI_0: + /* + * When the system is ready to go back to IWL_TI_0 + * we only have to call iwl_power_update_mode() to + * do so. + */ + break; + case IWL_TI_1: + tt->tt_power_mode = IWL_POWER_INDEX_3; + break; + case IWL_TI_2: + tt->tt_power_mode = IWL_POWER_INDEX_4; + break; + default: + tt->tt_power_mode = IWL_POWER_INDEX_5; + break; + } + mutex_lock(&priv->mutex); + if (iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + tt->state = old_state; + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + } else { + if (tt->state == IWL_TI_CT_KILL) + iwl_perform_ct_kill_task(priv, true); + else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) + iwl_perform_ct_kill_task(priv, false); + IWL_DEBUG_POWER(priv, "Temperature state changed %u\n", + tt->state); + IWL_DEBUG_POWER(priv, "Power Index change to %u\n", + tt->tt_power_mode); + } + mutex_unlock(&priv->mutex); + } +} + +/* + * Advance thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + * Actions include relaxing the power down sleep thresholds and + * decreasing the number of TX streams + * 3) Avoid throughput performance impact as much as possible + * + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int i; + bool changed = false; + enum iwl_tt_state old_state; + struct iwl_tt_trans *transaction; + + old_state = tt->state; + for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { + /* based on the current TT state, + * find the curresponding transaction table + * each table has (IWL_TI_STATE_MAX - 1) entries + * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) + * will advance to the correct table. + * then based on the current temperature + * find the next state need to transaction to + * go through all the possible (IWL_TI_STATE_MAX - 1) entries + * in the current table to see if transaction is needed + */ + transaction = tt->transaction + + ((old_state * (IWL_TI_STATE_MAX - 1)) + i); + if (temp >= transaction->tt_low && + temp <= transaction->tt_high) { +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_POWER(priv, + "Temperature increase %d " + "degree Celsius\n", + (temp - tt->tt_previous_temp)); + } + tt->tt_previous_temp = temp; +#endif + if (old_state != + transaction->next_state) { + changed = true; + tt->state = + transaction->next_state; + } + break; + } + } + if (changed) { + struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + + if (tt->state >= IWL_TI_1) { + /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ + tt->tt_power_mode = IWL_POWER_INDEX_5; + if (!iwl_ht_enabled(priv)) + /* disable HT */ + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + else { + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + } else { + /* + * restore system power setting -- it will be + * recalculated automatically. + */ + + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + mutex_lock(&priv->mutex); + if (iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + tt->state = old_state; + } else { + IWL_DEBUG_POWER(priv, + "Thermal Throttling to new state: %u\n", + tt->state); + if (old_state != IWL_TI_CT_KILL && + tt->state == IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, true); + + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, false); + } + } + mutex_unlock(&priv->mutex); + } +} + +/* Card State Notification indicated reach critical temperature + * if PSP not enable, no Thermal Throttling function will be performed + * just set the GP1 bit to acknowledge the event + * otherwise, go into IWL_TI_CT_KILL state + * since Card State Notification will not provide any temperature reading + * for Legacy mode + * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() + * for advance mode + * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state + */ +static void iwl_bg_ct_enter(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + if (tt->state != IWL_TI_CT_KILL) { + IWL_ERR(priv, "Device reached critical temperature " + "- ucode going to sleep!\n"); + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_MINIMAL_POWER_THRESHOLD); + else + iwl_advance_tt_handler(priv, + CT_KILL_THRESHOLD + 1); + } +} + +/* Card State Notification indicated out of critical temperature + * since Card State Notification will not provide any temperature reading + * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature + * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state + */ +static void iwl_bg_ct_exit(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + /* stop ct_kill_exit_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + + if (tt->state == IWL_TI_CT_KILL) { + IWL_ERR(priv, + "Device temperature below critical" + "- ucode awake!\n"); + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_REDUCED_PERFORMANCE_THRESHOLD_2); + else + iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD); + } +} + +void iwl_tt_enter_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_POWER(priv, "Queueing critical temperature enter.\n"); + queue_work(priv->workqueue, &priv->ct_enter); +} +EXPORT_SYMBOL(iwl_tt_enter_ct_kill); + +void iwl_tt_exit_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_POWER(priv, "Queueing critical temperature exit.\n"); + queue_work(priv->workqueue, &priv->ct_exit); +} +EXPORT_SYMBOL(iwl_tt_exit_ct_kill); + +static void iwl_bg_tt_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); + s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */ + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) + temp = KELVIN_TO_CELSIUS(priv->temperature); + + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, temp); + else + iwl_advance_tt_handler(priv, temp); +} + +void iwl_tt_handler(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_POWER(priv, "Queueing thermal throttling work.\n"); + queue_work(priv->workqueue, &priv->tt_work); +} +EXPORT_SYMBOL(iwl_tt_handler); + +/* Thermal throttling initialization + * For advance thermal throttling: + * Initialize Thermal Index and temperature threshold table + * Initialize thermal throttling restriction table + */ +void iwl_tt_initialize(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); + struct iwl_tt_trans *transaction; + + IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling \n"); + + memset(tt, 0, sizeof(struct iwl_tt_mgmt)); + + tt->state = IWL_TI_0; + init_timer(&priv->thermal_throttle.ct_kill_exit_tm); + priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; + priv->thermal_throttle.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill; + + /* setup deferred ct kill work */ + INIT_WORK(&priv->tt_work, iwl_bg_tt_work); + INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); + INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); + + switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_6x00: + case CSR_HW_REV_TYPE_6x50: + IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n"); + tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX, GFP_KERNEL); + tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) * + IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1), + GFP_KERNEL); + if (!tt->restriction || !tt->transaction) { + IWL_ERR(priv, "Fallback to Legacy Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } else { + transaction = tt->transaction + + (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_0[0], size); + transaction = tt->transaction + + (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_1[0], size); + transaction = tt->transaction + + (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_2[0], size); + transaction = tt->transaction + + (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_3[0], size); + size = sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX; + memcpy(tt->restriction, + &restriction_range[0], size); + priv->thermal_throttle.advanced_tt = true; + } + break; + default: + IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + break; + } +} +EXPORT_SYMBOL(iwl_tt_initialize); + +/* cleanup thermal throttling management related memory and timer */ +void iwl_tt_exit(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + /* stop ct_kill_exit_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + cancel_work_sync(&priv->tt_work); + cancel_work_sync(&priv->ct_enter); + cancel_work_sync(&priv->ct_exit); + + if (priv->thermal_throttle.advanced_tt) { + /* free advance thermal throttling memory */ + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } +} +EXPORT_SYMBOL(iwl_tt_exit); /* initialize to default */ void iwl_power_initialize(struct iwl_priv *priv) { - iwl_power_init_handle(priv); - priv->power_data.user_power_setting = IWL_POWER_INDEX_1; - /* default to disabled until mac80211 says otherwise */ - priv->power_data.power_disabled = 1; + u16 lctl = iwl_pcie_link_ctl(priv); + + priv->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); + + priv->power_data.debug_sleep_level_override = -1; + + memset(&priv->power_data.sleep_cmd, 0, + sizeof(priv->power_data.sleep_cmd)); } EXPORT_SYMBOL(iwl_power_initialize); diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h index 37ba3bb7a25..df6f6a49712 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ b/drivers/net/wireless/iwlwifi/iwl-power.h @@ -28,13 +28,91 @@ #ifndef __iwl_power_setting_h__ #define __iwl_power_setting_h__ -#include <net/mac80211.h> #include "iwl-commands.h" -struct iwl_priv; +#define IWL_ABSOLUTE_ZERO 0 +#define IWL_ABSOLUTE_MAX 0xFFFFFFFF +#define IWL_TT_INCREASE_MARGIN 5 -enum { - IWL_POWER_MODE_CAM, /* Continuously Aware Mode, always on */ +enum iwl_antenna_ok { + IWL_ANT_OK_NONE, + IWL_ANT_OK_SINGLE, + IWL_ANT_OK_MULTI, +}; + +/* Thermal Throttling State Machine states */ +enum iwl_tt_state { + IWL_TI_0, /* normal temperature, system power state */ + IWL_TI_1, /* high temperature detect, low power state */ + IWL_TI_2, /* higher temperature detected, lower power state */ + IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ + IWL_TI_STATE_MAX +}; + +/** + * struct iwl_tt_restriction - Thermal Throttling restriction table + * @tx_stream: number of tx stream allowed + * @is_ht: ht enable/disable + * @rx_stream: number of rx stream allowed + * + * This table is used by advance thermal throttling management + * based on the current thermal throttling state, and determines + * the number of tx/rx streams and the status of HT operation. + */ +struct iwl_tt_restriction { + enum iwl_antenna_ok tx_stream; + enum iwl_antenna_ok rx_stream; + bool is_ht; +}; + +/** + * struct iwl_tt_trans - Thermal Throttling transaction table + * @next_state: next thermal throttling mode + * @tt_low: low temperature threshold to change state + * @tt_high: high temperature threshold to change state + * + * This is used by the advanced thermal throttling algorithm + * to determine the next thermal state to go based on the + * current temperature. + */ +struct iwl_tt_trans { + enum iwl_tt_state next_state; + u32 tt_low; + u32 tt_high; +}; + +/** + * struct iwl_tt_mgnt - Thermal Throttling Management structure + * @advanced_tt: advanced thermal throttle required + * @state: current Thermal Throttling state + * @tt_power_mode: Thermal Throttling power mode index + * being used to set power level when + * when thermal throttling state != IWL_TI_0 + * the tt_power_mode should set to different + * power mode based on the current tt state + * @tt_previous_temperature: last measured temperature + * @iwl_tt_restriction: ptr to restriction tbl, used by advance + * thermal throttling to determine how many tx/rx streams + * should be used in tt state; and can HT be enabled or not + * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling + * state transaction + * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature + * @ct_kill_exit_tm: timer to exit thermal kill + */ +struct iwl_tt_mgmt { + enum iwl_tt_state state; + bool advanced_tt; + u8 tt_power_mode; + bool ct_kill_toggle; +#ifdef CONFIG_IWLWIFI_DEBUG + s32 tt_previous_temp; +#endif + struct iwl_tt_restriction *restriction; + struct iwl_tt_trans *transaction; + struct timer_list ct_kill_exit_tm; +}; + +enum iwl_power_level { IWL_POWER_INDEX_1, IWL_POWER_INDEX_2, IWL_POWER_INDEX_3, @@ -43,26 +121,23 @@ enum { IWL_POWER_NUM }; -/* Power management (not Tx power) structures */ - -struct iwl_power_vec_entry { - struct iwl_powertable_cmd cmd; - u8 no_dtim; -}; - struct iwl_power_mgr { - struct iwl_power_vec_entry pwr_range_0[IWL_POWER_NUM]; - struct iwl_power_vec_entry pwr_range_1[IWL_POWER_NUM]; - struct iwl_power_vec_entry pwr_range_2[IWL_POWER_NUM]; - u32 dtim_period; - /* final power level that used to calculate final power command */ - u8 power_mode; - u8 user_power_setting; /* set by user through sysfs */ - u8 power_disabled; /* set by mac80211's CONF_PS */ + struct iwl_powertable_cmd sleep_cmd; + int debug_sleep_level_override; + bool pci_pm; }; int iwl_power_update_mode(struct iwl_priv *priv, bool force); -int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode); +bool iwl_ht_enabled(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); +void iwl_tt_enter_ct_kill(struct iwl_priv *priv); +void iwl_tt_exit_ct_kill(struct iwl_priv *priv); +void iwl_tt_handler(struct iwl_priv *priv); +void iwl_tt_initialize(struct iwl_priv *priv); +void iwl_tt_exit(struct iwl_priv *priv); void iwl_power_initialize(struct iwl_priv *priv); +extern bool no_sleep_autoadjust; + #endif /* __iwl_power_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 3b9cac3fd21..d393e8f0210 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -80,6 +80,8 @@ #define APMG_RFKILL_REG (APMG_BASE + 0x0014) #define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) #define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) +#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) +#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) #define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) #define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) @@ -91,7 +93,8 @@ #define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) #define APMG_PS_CTRL_VAL_PWR_SRC_MAX (0x01000000) /* 3945 only */ #define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) - +#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ +#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 2b8d40b37a1..e34d3fcb6c3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -406,7 +406,6 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); } -EXPORT_SYMBOL(iwl_rx_queue_reset); int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) { @@ -540,13 +539,14 @@ void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", - (int)sizeof(priv->statistics), pkt->len); + (int)sizeof(priv->statistics), + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); change = ((priv->statistics.general.temperature != pkt->u.stats.general.temperature) || ((priv->statistics.flag & - STATISTICS_REPLY_FLG_FAT_MODE_MSK) != - (pkt->u.stats.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK))); + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); @@ -646,7 +646,7 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv, u32 tsf_low; int rssi; - if (likely(!(priv->debug_level & IWL_DL_RX))) + if (likely(!(iwl_get_debug_level(priv) & IWL_DL_RX))) return; /* MAC header */ @@ -746,14 +746,6 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv, } #endif -static void iwl_update_rx_stats(struct iwl_priv *priv, u16 fc, u16 len) -{ - /* 0 - mgmt, 1 - cnt, 2 - data */ - int idx = (fc & IEEE80211_FCTL_FTYPE) >> 2; - priv->rx_stats[idx].cnt++; - priv->rx_stats[idx].bytes += len; -} - /* * returns non-zero if packet should be dropped */ @@ -862,61 +854,12 @@ static u32 iwl_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) } static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, - int include_phy, - struct iwl_rx_mem_buffer *rxb, - struct ieee80211_rx_status *stats) + struct ieee80211_hdr *hdr, + u16 len, + u32 ampdu_status, + struct iwl_rx_mem_buffer *rxb, + struct ieee80211_rx_status *stats) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl_rx_phy_res *rx_start = (include_phy) ? - (struct iwl_rx_phy_res *)&(pkt->u.raw[0]) : NULL; - struct ieee80211_hdr *hdr; - u16 len; - __le32 *rx_end; - unsigned int skblen; - u32 ampdu_status; - u32 ampdu_status_legacy; - - if (!include_phy && priv->last_phy_res[0]) - rx_start = (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; - - if (!rx_start) { - IWL_ERR(priv, "MPDU frame without a PHY data\n"); - return; - } - if (include_phy) { - hdr = (struct ieee80211_hdr *)((u8 *) &rx_start[1] + - rx_start->cfg_phy_cnt); - - len = le16_to_cpu(rx_start->byte_count); - - rx_end = (__le32 *)((u8 *) &pkt->u.raw[0] + - sizeof(struct iwl_rx_phy_res) + - rx_start->cfg_phy_cnt + len); - - } else { - struct iwl4965_rx_mpdu_res_start *amsdu = - (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; - - hdr = (struct ieee80211_hdr *)(pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start)); - len = le16_to_cpu(amsdu->byte_count); - rx_start->byte_count = amsdu->byte_count; - rx_end = (__le32 *) (((u8 *) hdr) + len); - } - - ampdu_status = le32_to_cpu(*rx_end); - skblen = ((u8 *) rx_end - (u8 *) &pkt->u.raw[0]) + sizeof(u32); - - if (!include_phy) { - /* New status scheme, need to translate */ - ampdu_status_legacy = ampdu_status; - ampdu_status = iwl_translate_rx_status(priv, ampdu_status); - } - - /* start from MAC */ - skb_reserve(rxb->skb, (void *)hdr - (void *)pkt); - skb_put(rxb->skb, len); /* end where data ends */ - /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { IWL_DEBUG_DROP_LIMIT(priv, @@ -924,15 +867,18 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, return; } - hdr = (struct ieee80211_hdr *)rxb->skb->data; - - /* in case of HW accelerated crypto and bad decryption, drop */ - if (!priv->hw_params.sw_crypto && + /* In case of HW accelerated crypto and bad decryption, drop */ + if (!priv->cfg->mod_params->sw_crypto && iwl_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; - iwl_update_rx_stats(priv, le16_to_cpu(hdr->frame_control), len); - ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); + /* Resize SKB from mac header to end of packet */ + skb_reserve(rxb->skb, (void *)hdr - (void *)rxb->skb->data); + skb_put(rxb->skb, len); + + iwl_update_stats(priv, false, hdr->frame_control, len); + memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats)); + ieee80211_rx_irqsafe(priv->hw, rxb->skb); priv->alloc_rxb_skb--; rxb->skb = NULL; } @@ -963,25 +909,67 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - /* Use phy data (Rx signal strength, etc.) contained within - * this rx packet for legacy frames, - * or phy data cached from REPLY_RX_PHY_CMD for HT frames. */ - int include_phy = (pkt->hdr.cmd == REPLY_RX); - struct iwl_rx_phy_res *rx_start = (include_phy) ? - (struct iwl_rx_phy_res *)&(pkt->u.raw[0]) : - (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; - __le32 *rx_end; - unsigned int len = 0; + struct iwl_rx_phy_res *phy_res; + __le32 rx_pkt_status; + struct iwl4965_rx_mpdu_res_start *amsdu; + u32 len; + u32 ampdu_status; u16 fc; - u8 network_packet; + u32 rate_n_flags; + + /** + * REPLY_RX and REPLY_RX_MPDU_CMD are handled differently. + * REPLY_RX: physical layer info is in this buffer + * REPLY_RX_MPDU_CMD: physical layer info was sent in separate + * command and cached in priv->last_phy_res + * + * Here we set up local variables depending on which command is + * received. + */ + if (pkt->hdr.cmd == REPLY_RX) { + phy_res = (struct iwl_rx_phy_res *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt); + + len = le16_to_cpu(phy_res->byte_count); + rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt + len); + ampdu_status = le32_to_cpu(rx_pkt_status); + } else { + if (!priv->last_phy_res[0]) { + IWL_ERR(priv, "MPDU frame without cached PHY data\n"); + return; + } + phy_res = (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; + amsdu = (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*amsdu) + len); + ampdu_status = iwl_translate_rx_status(priv, + le32_to_cpu(rx_pkt_status)); + } - rx_status.mactime = le64_to_cpu(rx_start->timestamp); + if ((unlikely(phy_res->cfg_phy_cnt > 20))) { + IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n", + phy_res->cfg_phy_cnt); + return; + } + + if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(rx_pkt_status)); + return; + } + + /* rx_status carries information about the packet to mac80211 */ + rx_status.mactime = le64_to_cpu(phy_res->timestamp); rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(rx_start->channel)); - rx_status.band = (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel)); + rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; rx_status.rate_idx = - iwl_hwrate_to_plcp_idx(le32_to_cpu(rx_start->rate_n_flags)); + iwl_hwrate_to_plcp_idx(le32_to_cpu(phy_res->rate_n_flags)); if (rx_status.band == IEEE80211_BAND_5GHZ) rx_status.rate_idx -= IWL_FIRST_OFDM_RATE; @@ -991,54 +979,10 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, * this W/A doesn't propagate it to the mac80211 */ /*rx_status.flag |= RX_FLAG_TSFT;*/ - if ((unlikely(rx_start->cfg_phy_cnt > 20))) { - IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n", - rx_start->cfg_phy_cnt); - return; - } - - if (!include_phy) { - if (priv->last_phy_res[0]) - rx_start = (struct iwl_rx_phy_res *) - &priv->last_phy_res[1]; - else - rx_start = NULL; - } - - if (!rx_start) { - IWL_ERR(priv, "MPDU frame without a PHY data\n"); - return; - } - - if (include_phy) { - header = (struct ieee80211_hdr *)((u8 *) &rx_start[1] - + rx_start->cfg_phy_cnt); - - len = le16_to_cpu(rx_start->byte_count); - rx_end = (__le32 *)(pkt->u.raw + rx_start->cfg_phy_cnt + - sizeof(struct iwl_rx_phy_res) + len); - } else { - struct iwl4965_rx_mpdu_res_start *amsdu = - (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; - - header = (void *)(pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start)); - len = le16_to_cpu(amsdu->byte_count); - rx_end = (__le32 *) (pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start) + len); - } - - if (!(*rx_end & RX_RES_STATUS_NO_CRC32_ERROR) || - !(*rx_end & RX_RES_STATUS_NO_RXE_OVERFLOW)) { - IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", - le32_to_cpu(*rx_end)); - return; - } - - priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp); + priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); /* Find max signal strength (dBm) among 3 antenna/receiver chains */ - rx_status.signal = iwl_calc_rssi(priv, rx_start); + rx_status.signal = iwl_calc_rssi(priv, phy_res); /* Meaningful noise values are available only from beacon statistics, * which are gathered only when associated, and indicate noise @@ -1058,13 +1002,14 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, if (!iwl_is_associated(priv)) priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; - /* Set "1" to report good data frames in groups of 100 */ #ifdef CONFIG_IWLWIFI_DEBUG - if (unlikely(priv->debug_level & IWL_DL_RX)) - iwl_dbg_report_frame(priv, rx_start, len, header, 1); + /* Set "1" to report good data frames in groups of 100 */ + if (unlikely(iwl_get_debug_level(priv) & IWL_DL_RX)) + iwl_dbg_report_frame(priv, phy_res, len, header, 1); #endif + iwl_dbg_log_rx_data_frame(priv, len, header); IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, noise %d, qual %d, TSF %llu\n", - rx_status.signal, rx_status.noise, rx_status.signal, + rx_status.signal, rx_status.noise, rx_status.qual, (unsigned long long)rx_status.mactime); /* @@ -1080,18 +1025,27 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, * new 802.11n radiotap field "RX chains" that is defined * as a bitmask. */ - rx_status.antenna = le16_to_cpu(rx_start->phy_flags & - RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; + rx_status.antenna = + le16_to_cpu(phy_res->phy_flags & RX_RES_PHY_FLAGS_ANTENNA_MSK) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; /* set the preamble flag if appropriate */ - if (rx_start->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) rx_status.flag |= RX_FLAG_SHORTPRE; - network_packet = iwl_is_network_packet(priv, header); - if (network_packet) { + /* Set up the HT phy flags */ + rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); + if (rate_n_flags & RATE_MCS_HT_MSK) + rx_status.flag |= RX_FLAG_HT; + if (rate_n_flags & RATE_MCS_HT40_MSK) + rx_status.flag |= RX_FLAG_40MHZ; + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status.flag |= RX_FLAG_SHORT_GI; + + if (iwl_is_network_packet(priv, header)) { priv->last_rx_rssi = rx_status.signal; priv->last_beacon_time = priv->ucode_beacon_time; - priv->last_tsf = le64_to_cpu(rx_start->timestamp); + priv->last_tsf = le64_to_cpu(phy_res->timestamp); } fc = le16_to_cpu(header->frame_control); @@ -1103,8 +1057,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, header->addr2); /* fall through */ default: - iwl_pass_packet_to_mac80211(priv, include_phy, rxb, - &rx_status); + iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, + rxb, &rx_status); break; } diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index e26875dbe85..4f3a108fa99 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -109,13 +109,13 @@ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) } EXPORT_SYMBOL(iwl_scan_cancel_timeout); -int iwl_send_scan_abort(struct iwl_priv *priv) +static int iwl_send_scan_abort(struct iwl_priv *priv) { int ret = 0; struct iwl_rx_packet *res; struct iwl_host_cmd cmd = { .id = REPLY_SCAN_ABORT_CMD, - .meta.flags = CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; /* If there isn't a scan actively going on in the hardware @@ -132,7 +132,7 @@ int iwl_send_scan_abort(struct iwl_priv *priv) return ret; } - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->u.status != CAN_ABORT_STATUS) { /* The scan abort will return 1 for success or * 2 for "failure". A failure condition can be @@ -146,11 +146,10 @@ int iwl_send_scan_abort(struct iwl_priv *priv) } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return ret; } -EXPORT_SYMBOL(iwl_send_scan_abort); /* Service response to REPLY_SCAN_CMD (0x80) */ static void iwl_rx_reply_scan(struct iwl_priv *priv, @@ -322,7 +321,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, u8 is_active, u8 n_probes, struct iwl_scan_channel *scan_ch) { - const struct ieee80211_channel *channels = NULL; + struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; const struct iwl_channel_info *ch_info; u16 passive_dwell = 0; @@ -334,20 +333,19 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, if (!sband) return 0; - channels = sband->channels; - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); passive_dwell = iwl_get_passive_dwell_time(priv, band); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; - for (i = 0, added = 0; i < sband->n_channels; i++) { - if (channels[i].flags & IEEE80211_CHAN_DISABLED) + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) continue; - channel = - ieee80211_frequency_to_channel(channels[i].center_freq); + channel = ieee80211_frequency_to_channel(chan->center_freq); scan_ch->channel = cpu_to_le16(channel); ch_info = iwl_get_channel_info(priv, band, channel); @@ -358,7 +356,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, } if (!is_active || is_channel_passive(ch_info) || - (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) + (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; @@ -405,7 +403,7 @@ void iwl_init_scan_params(struct iwl_priv *priv) priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; } -int iwl_scan_initiate(struct iwl_priv *priv) +static int iwl_scan_initiate(struct iwl_priv *priv) { if (!iwl_is_ready_rf(priv)) { IWL_DEBUG_SCAN(priv, "Aborting scan due to not ready.\n"); @@ -423,10 +421,6 @@ int iwl_scan_initiate(struct iwl_priv *priv) } IWL_DEBUG_INFO(priv, "Starting scan...\n"); - if (priv->cfg->sku & IWL_SKU_G) - priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); - if (priv->cfg->sku & IWL_SKU_A) - priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); set_bit(STATUS_SCANNING, &priv->status); priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -435,7 +429,6 @@ int iwl_scan_initiate(struct iwl_priv *priv) return 0; } -EXPORT_SYMBOL(iwl_scan_initiate); #define IWL_DELAY_NEXT_SCAN (HZ*2) @@ -444,7 +437,7 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, { unsigned long flags; struct iwl_priv *priv = hw->priv; - int ret; + int ret, i; IWL_DEBUG_MAC80211(priv, "enter\n"); @@ -478,6 +471,10 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, goto out_unlock; } + priv->scan_bands = 0; + for (i = 0; i < req->n_channels; i++) + priv->scan_bands |= BIT(req->channels[i]->band); + priv->scan_request = req; ret = iwl_scan_initiate(priv); @@ -570,7 +567,7 @@ static void iwl_bg_request_scan(struct work_struct *data) struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = sizeof(struct iwl_scan_cmd), - .meta.flags = CMD_SIZE_HUGE, + .flags = CMD_SIZE_HUGE, }; struct iwl_scan_cmd *scan; struct ieee80211_conf *conf = NULL; @@ -799,7 +796,8 @@ void iwl_bg_abort_scan(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); - if (!iwl_is_ready(priv)) + if (!test_bit(STATUS_READY, &priv->status) || + !test_bit(STATUS_GEO_CONFIGURED, &priv->status)) return; mutex_lock(&priv->mutex); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index ffd5c61a755..a2b9ec82b96 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -97,8 +97,9 @@ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) spin_unlock_irqrestore(&priv->sta_lock, flags); } -static int iwl_add_sta_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb) +static void iwl_add_sta_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { struct iwl_rx_packet *res = NULL; struct iwl_addsta_cmd *addsta = @@ -107,14 +108,14 @@ static int iwl_add_sta_callback(struct iwl_priv *priv, if (!skb) { IWL_ERR(priv, "Error: Response NULL in REPLY_ADD_STA.\n"); - return 1; + return; } res = (struct iwl_rx_packet *)skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", res->hdr.flags); - return 1; + return; } switch (res->u.add_sta.status) { @@ -126,9 +127,6 @@ static int iwl_add_sta_callback(struct iwl_priv *priv, res->u.add_sta.status); break; } - - /* We didn't cache the SKB; let the caller free it */ - return 1; } int iwl_send_add_sta(struct iwl_priv *priv, @@ -139,14 +137,14 @@ int iwl_send_add_sta(struct iwl_priv *priv, u8 data[sizeof(*sta)]; struct iwl_host_cmd cmd = { .id = REPLY_ADD_STA, - .meta.flags = flags, + .flags = flags, .data = data, }; if (flags & CMD_ASYNC) - cmd.meta.u.callback = iwl_add_sta_callback; + cmd.callback = iwl_add_sta_callback; else - cmd.meta.flags |= CMD_WANT_SKB; + cmd.flags |= CMD_WANT_SKB; cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); ret = iwl_send_cmd(priv, &cmd); @@ -154,7 +152,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, if (ret || (flags & CMD_ASYNC)) return ret; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", res->hdr.flags); @@ -175,7 +173,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return ret; } @@ -216,10 +214,10 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, sta_flags |= cpu_to_le32( (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - if (iwl_is_fat_tx_allowed(priv, sta_ht_inf)) - sta_flags |= STA_FLG_FAT_EN_MSK; + if (iwl_is_ht40_tx_allowed(priv, sta_ht_inf)) + sta_flags |= STA_FLG_HT40_EN_MSK; else - sta_flags &= ~STA_FLG_FAT_EN_MSK; + sta_flags &= ~STA_FLG_HT40_EN_MSK; priv->stations[index].sta.station_flags = sta_flags; done: @@ -324,8 +322,9 @@ static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr) spin_unlock_irqrestore(&priv->sta_lock, flags); } -static int iwl_remove_sta_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb) +static void iwl_remove_sta_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { struct iwl_rx_packet *res = NULL; struct iwl_rem_sta_cmd *rm_sta = @@ -334,14 +333,14 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv, if (!skb) { IWL_ERR(priv, "Error: Response NULL in REPLY_REMOVE_STA.\n"); - return 1; + return; } res = (struct iwl_rx_packet *)skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", res->hdr.flags); - return 1; + return; } switch (res->u.rem_sta.status) { @@ -352,9 +351,6 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv, IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); break; } - - /* We didn't cache the SKB; let the caller free it */ - return 1; } static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, @@ -368,7 +364,7 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = sizeof(struct iwl_rem_sta_cmd), - .meta.flags = flags, + .flags = flags, .data = &rm_sta_cmd, }; @@ -377,15 +373,15 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, memcpy(&rm_sta_cmd.addr, addr , ETH_ALEN); if (flags & CMD_ASYNC) - cmd.meta.u.callback = iwl_remove_sta_callback; + cmd.callback = iwl_remove_sta_callback; else - cmd.meta.flags |= CMD_WANT_SKB; + cmd.flags |= CMD_WANT_SKB; ret = iwl_send_cmd(priv, &cmd); if (ret || (flags & CMD_ASYNC)) return ret; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", res->hdr.flags); @@ -406,7 +402,7 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return ret; } @@ -468,7 +464,6 @@ out: spin_unlock_irqrestore(&priv->sta_lock, flags); return ret; } -EXPORT_SYMBOL(iwl_remove_station); /** * iwl_clear_stations_table - Clear the driver's station table @@ -525,7 +520,7 @@ int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) struct iwl_host_cmd cmd = { .id = REPLY_WEPKEY, .data = wep_cmd, - .meta.flags = CMD_ASYNC, + .flags = CMD_SYNC, }; memset(wep_cmd, 0, cmd_size + @@ -930,7 +925,7 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, .len = sizeof(struct iwl_link_quality_cmd), - .meta.flags = flags, + .flags = flags, .data = lq, }; @@ -1056,11 +1051,10 @@ EXPORT_SYMBOL(iwl_rxon_add_station); int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) { int sta_id; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; /* If this frame is broadcast or management, use broadcast station id */ - if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) || - is_multicast_ether_addr(hdr->addr1)) + if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) return priv->hw_params.bcast_sta_id; switch (priv->iw_mode) { diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 2e89040e63b..7bc9c0039f7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -141,7 +141,7 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) priv->cfg->ops->lib->txq_free_tfd(priv, txq); - len = sizeof(struct iwl_cmd) * q->n_window; + len = sizeof(struct iwl_device_cmd) * q->n_window; /* De-alloc array of command/tx buffers */ for (i = 0; i < TFD_TX_CMD_SLOTS; i++) @@ -156,6 +156,12 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) kfree(txq->txb); txq->txb = NULL; + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } @@ -179,7 +185,7 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) if (q->n_bd == 0) return; - len = sizeof(struct iwl_cmd) * q->n_window; + len = sizeof(struct iwl_device_cmd) * q->n_window; len += IWL_MAX_SCAN_SIZE; /* De-alloc array of command/tx buffers */ @@ -318,6 +324,7 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, { int i, len; int ret; + int actual_slots = slots_num; /* * Alloc buffer array for commands (Tx or other types of commands). @@ -327,14 +334,22 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, * For normal Tx queues (all other queues), no super-size command * space is needed. */ - len = sizeof(struct iwl_cmd); - for (i = 0; i <= slots_num; i++) { - if (i == slots_num) { - if (txq_id == IWL_CMD_QUEUE_NUM) - len += IWL_MAX_SCAN_SIZE; - else - continue; - } + if (txq_id == IWL_CMD_QUEUE_NUM) + actual_slots++; + + txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * actual_slots, + GFP_KERNEL); + txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * actual_slots, + GFP_KERNEL); + + if (!txq->meta || !txq->cmd) + goto out_free_arrays; + + len = sizeof(struct iwl_device_cmd); + for (i = 0; i < actual_slots; i++) { + /* only happens for cmd queue */ + if (i == slots_num) + len += IWL_MAX_SCAN_SIZE; txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) @@ -348,6 +363,10 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, txq->need_update = 0; + /* aggregation TX queues will get their ID when aggregation begins */ + if (txq_id <= IWL_TX_FIFO_AC3) + txq->swq_id = txq_id; + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); @@ -360,15 +379,12 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, return 0; err: - for (i = 0; i < slots_num; i++) { + for (i = 0; i < actual_slots; i++) kfree(txq->cmd[i]); - txq->cmd[i] = NULL; - } +out_free_arrays: + kfree(txq->meta); + kfree(txq->cmd); - if (txq_id == IWL_CMD_QUEUE_NUM) { - kfree(txq->cmd[slots_num]); - txq->cmd[slots_num] = NULL; - } return -ENOMEM; } EXPORT_SYMBOL(iwl_tx_queue_init); @@ -652,14 +668,6 @@ static void iwl_tx_cmd_build_hwcrypto(struct iwl_priv *priv, } } -static void iwl_update_tx_stats(struct iwl_priv *priv, u16 fc, u16 len) -{ - /* 0 - mgmt, 1 - cnt, 2 - data */ - int idx = (fc & IEEE80211_FCTL_FTYPE) >> 2; - priv->tx_stats[idx].cnt++; - priv->tx_stats[idx].bytes += len; -} - /* * start REPLY_TX command process */ @@ -669,7 +677,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_tx_queue *txq; struct iwl_queue *q; - struct iwl_cmd *out_cmd; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; struct iwl_tx_cmd *tx_cmd; int swq_id, txq_id; dma_addr_t phys_addr; @@ -709,10 +718,9 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif - /* drop all data frame if we are not associated */ + /* drop all non-injected data frame if we are not associated */ if (ieee80211_is_data(fc) && - (!iwl_is_monitor_mode(priv) || - !(info->flags & IEEE80211_TX_CTL_INJECTED)) && /* packet injection */ + !(info->flags & IEEE80211_TX_CTL_INJECTED) && (!iwl_is_associated(priv) || ((priv->iw_mode == NL80211_IFTYPE_STATION) && !priv->assoc_id) || !priv->assoc_station_added)) { @@ -723,7 +731,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); /* Find (or create) index into station table for destination station */ - sta_id = iwl_get_sta_id(priv, hdr); + if (info->flags & IEEE80211_TX_CTL_INJECTED) + sta_id = priv->hw_params.bcast_sta_id; + else + sta_id = iwl_get_sta_id(priv, hdr); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -732,11 +743,12 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); - swq_id = skb_get_queue_mapping(skb); - txq_id = swq_id; + txq_id = skb_get_queue_mapping(skb); if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (unlikely(tid >= MAX_TID_COUNT)) + goto drop_unlock; seq_number = priv->stations[sta_id].tid[tid].seq_number; seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl = hdr->seq_ctrl & @@ -744,15 +756,13 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this <sta,tid> */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) { + if (info->flags & IEEE80211_TX_CTL_AMPDU) txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; - swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id); - } } txq = &priv->txq[txq_id]; + swq_id = txq->swq_id; q = &txq->q; - txq->swq_id = swq_id; if (unlikely(iwl_queue_space(q) < q->high_mark)) goto drop_unlock; @@ -766,6 +776,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[q->write_ptr]; + out_meta = &txq->meta[q->write_ptr]; tx_cmd = &out_cmd->cmd.tx; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd)); @@ -793,12 +804,12 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* TODO need this for burst mode later on */ iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, sta_id); + iwl_dbg_log_tx_data_frame(priv, len, hdr); /* set is_hcca to 0; it probably will never be implemented */ iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0); - iwl_update_tx_stats(priv, le16_to_cpu(fc), len); - + iwl_update_stats(priv, true, fc, len); /* * Use the first empty entry in this queue's command buffer array * to contain the Tx command and MAC header concatenated together @@ -828,8 +839,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) txcmd_phys = pci_map_single(priv->pci_dev, &out_cmd->hdr, len, PCI_DMA_BIDIRECTIONAL); - pci_unmap_addr_set(&out_cmd->meta, mapping, txcmd_phys); - pci_unmap_len_set(&out_cmd->meta, len, len); + pci_unmap_addr_set(out_meta, mapping, txcmd_phys); + pci_unmap_len_set(out_meta, len, len); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, @@ -922,7 +933,8 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; struct iwl_queue *q = &txq->q; - struct iwl_cmd *out_cmd; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; int len, ret; @@ -936,25 +948,32 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then * we will need to increase the size of the TFD entries */ BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && - !(cmd->meta.flags & CMD_SIZE_HUGE)); + !(cmd->flags & CMD_SIZE_HUGE)); if (iwl_is_rfkill(priv)) { - IWL_DEBUG_INFO(priv, "Not sending command - RF KILL"); + IWL_DEBUG_INFO(priv, "Not sending command - RF KILL\n"); return -EIO; } - if (iwl_queue_space(q) < ((cmd->meta.flags & CMD_ASYNC) ? 2 : 1)) { + if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { IWL_ERR(priv, "No space for Tx\n"); return -ENOSPC; } spin_lock_irqsave(&priv->hcmd_lock, flags); - idx = get_cmd_index(q, q->write_ptr, cmd->meta.flags & CMD_SIZE_HUGE); + idx = get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; + + memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ + out_meta->flags = cmd->flags; + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + if (cmd->flags & CMD_ASYNC) + out_meta->callback = cmd->callback; out_cmd->hdr.cmd = cmd->id; - memcpy(&out_cmd->meta, &cmd->meta, sizeof(cmd->meta)); memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); /* At this point, the out_cmd now has all of the incoming cmd @@ -963,9 +982,9 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(IWL_CMD_QUEUE_NUM) | INDEX_TO_SEQ(q->write_ptr)); - if (out_cmd->meta.flags & CMD_SIZE_HUGE) + if (cmd->flags & CMD_SIZE_HUGE) out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; - len = sizeof(struct iwl_cmd) - sizeof(struct iwl_cmd_meta); + len = sizeof(struct iwl_device_cmd); len += (idx == TFD_CMD_SLOTS) ? IWL_MAX_SCAN_SIZE : 0; @@ -997,8 +1016,8 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) phys_addr = pci_map_single(priv->pci_dev, &out_cmd->hdr, fix_size, PCI_DMA_BIDIRECTIONAL); - pci_unmap_addr_set(&out_cmd->meta, mapping, phys_addr); - pci_unmap_len_set(&out_cmd->meta, len, fix_size); + pci_unmap_addr_set(out_meta, mapping, phys_addr); + pci_unmap_len_set(out_meta, len, fix_size); priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, phys_addr, fix_size, 1, @@ -1067,8 +1086,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, } pci_unmap_single(priv->pci_dev, - pci_unmap_addr(&txq->cmd[cmd_idx]->meta, mapping), - pci_unmap_len(&txq->cmd[cmd_idx]->meta, len), + pci_unmap_addr(&txq->meta[cmd_idx], mapping), + pci_unmap_len(&txq->meta[cmd_idx], len), PCI_DMA_BIDIRECTIONAL); for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; @@ -1099,7 +1118,8 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) int index = SEQ_TO_INDEX(sequence); int cmd_index; bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); - struct iwl_cmd *cmd; + struct iwl_device_cmd *cmd; + struct iwl_cmd_meta *meta; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced @@ -1109,24 +1129,24 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) txq_id, sequence, priv->txq[IWL_CMD_QUEUE_NUM].q.read_ptr, priv->txq[IWL_CMD_QUEUE_NUM].q.write_ptr)) { - iwl_print_hex_dump(priv, IWL_DL_INFO , rxb, 32); + iwl_print_hex_error(priv, pkt, 32); return; } cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge); cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; + meta = &priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_index]; /* Input error checking is done when commands are added to queue. */ - if (cmd->meta.flags & CMD_WANT_SKB) { - cmd->meta.source->u.skb = rxb->skb; - rxb->skb = NULL; - } else if (cmd->meta.u.callback && - !cmd->meta.u.callback(priv, cmd, rxb->skb)) + if (meta->flags & CMD_WANT_SKB) { + meta->source->reply_skb = rxb->skb; rxb->skb = NULL; + } else if (meta->callback) + meta->callback(priv, cmd, rxb->skb); iwl_hcmd_queue_reclaim(priv, txq_id, index, cmd_index); - if (!(cmd->meta.flags & CMD_ASYNC)) { + if (!(meta->flags & CMD_ASYNC)) { clear_bit(STATUS_HCMD_ACTIVE, &priv->status); wake_up_interruptible(&priv->wait_command_queue); } @@ -1189,6 +1209,7 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn) tid_data = &priv->stations[sta_id].tid[tid]; *ssn = SEQ_TO_SN(tid_data->seq_number); tid_data->agg.txq_id = txq_id; + priv->txq[txq_id].swq_id = iwl_virtual_agg_queue_num(tx_fifo, txq_id); spin_unlock_irqrestore(&priv->sta_lock, flags); ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo, @@ -1221,6 +1242,9 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) return -EINVAL; } + if (unlikely(tid >= MAX_TID_COUNT)) + return -EINVAL; + if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) tx_fifo_id = default_tid_to_tx_fifo[tid]; else diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 523843369ca..34790413ead 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -89,7 +89,7 @@ MODULE_LICENSE("GPL"); /* module parameters */ struct iwl_mod_params iwl3945_mod_params = { - .num_of_queues = IWL39_MAX_NUM_QUEUES, + .num_of_queues = IWL39_NUM_QUEUES, /* Not used */ .sw_crypto = 1, .restart_fw = 1, /* the rest are 0 by default */ @@ -361,79 +361,9 @@ static void iwl3945_unset_hw_params(struct iwl_priv *priv) priv->shared_phys); } -#define MAX_UCODE_BEACON_INTERVAL 1024 -#define INTEL_CONN_LISTEN_INTERVAL cpu_to_le16(0xA) - -static __le16 iwl3945_adjust_beacon_interval(u16 beacon_val) -{ - u16 new_val = 0; - u16 beacon_factor = 0; - - beacon_factor = - (beacon_val + MAX_UCODE_BEACON_INTERVAL) - / MAX_UCODE_BEACON_INTERVAL; - new_val = beacon_val / beacon_factor; - - return cpu_to_le16(new_val); -} - -static void iwl3945_setup_rxon_timing(struct iwl_priv *priv) -{ - u64 interval_tm_unit; - u64 tsf, result; - unsigned long flags; - struct ieee80211_conf *conf = NULL; - u16 beacon_int = 0; - - conf = ieee80211_get_hw_conf(priv->hw); - - spin_lock_irqsave(&priv->lock, flags); - priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp); - priv->rxon_timing.listen_interval = INTEL_CONN_LISTEN_INTERVAL; - - tsf = priv->timestamp; - - beacon_int = priv->beacon_int; - spin_unlock_irqrestore(&priv->lock, flags); - - if (priv->iw_mode == NL80211_IFTYPE_STATION) { - if (beacon_int == 0) { - priv->rxon_timing.beacon_interval = cpu_to_le16(100); - priv->rxon_timing.beacon_init_val = cpu_to_le32(102400); - } else { - priv->rxon_timing.beacon_interval = - cpu_to_le16(beacon_int); - priv->rxon_timing.beacon_interval = - iwl3945_adjust_beacon_interval( - le16_to_cpu(priv->rxon_timing.beacon_interval)); - } - - priv->rxon_timing.atim_window = 0; - } else { - priv->rxon_timing.beacon_interval = - iwl3945_adjust_beacon_interval( - priv->vif->bss_conf.beacon_int); - /* TODO: we need to get atim_window from upper stack - * for now we set to 0 */ - priv->rxon_timing.atim_window = 0; - } - - interval_tm_unit = - (le16_to_cpu(priv->rxon_timing.beacon_interval) * 1024); - result = do_div(tsf, interval_tm_unit); - priv->rxon_timing.beacon_init_val = - cpu_to_le32((u32) ((u64) interval_tm_unit - result)); - - IWL_DEBUG_ASSOC(priv, - "beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(priv->rxon_timing.beacon_interval), - le32_to_cpu(priv->rxon_timing.beacon_init_val), - le16_to_cpu(priv->rxon_timing.atim_window)); -} - static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, struct ieee80211_tx_info *info, - struct iwl_cmd *cmd, + struct iwl_device_cmd *cmd, struct sk_buff *skb_frag, int sta_id) { @@ -473,7 +403,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, * handle build REPLY_TX command notification. */ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv, - struct iwl_cmd *cmd, + struct iwl_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, u8 std_id) { @@ -546,7 +476,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) struct iwl3945_tx_cmd *tx; struct iwl_tx_queue *txq = NULL; struct iwl_queue *q = NULL; - struct iwl_cmd *out_cmd = NULL; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; dma_addr_t txcmd_phys; int txq_id = skb_get_queue_mapping(skb); @@ -587,9 +518,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif - /* drop all data frame if we are not associated */ + /* drop all non-injected data frame if we are not associated */ if (ieee80211_is_data(fc) && - (!iwl_is_monitor_mode(priv)) && /* packet injection */ + !(info->flags & IEEE80211_TX_CTL_INJECTED) && (!iwl_is_associated(priv) || ((priv->iw_mode == NL80211_IFTYPE_STATION) && !priv->assoc_id))) { IWL_DEBUG_DROP(priv, "Dropping - !iwl_is_associated\n"); @@ -601,7 +532,10 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); /* Find (or create) index into station table for destination station */ - sta_id = iwl_get_sta_id(priv, hdr); + if (info->flags & IEEE80211_TX_CTL_INJECTED) + sta_id = priv->hw_params.bcast_sta_id; + else + sta_id = iwl_get_sta_id(priv, hdr); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -613,6 +547,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (unlikely(tid >= MAX_TID_COUNT)) + goto drop; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; hdr->seq_ctrl = cpu_to_le16(seq_number) | @@ -635,6 +571,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Init first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; tx = (struct iwl3945_tx_cmd *)out_cmd->cmd.payload; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx, 0, sizeof(*tx)); @@ -666,7 +603,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) len = (u16)skb->len; tx->len = cpu_to_le16(len); - + iwl_dbg_log_tx_data_frame(priv, len, hdr); + iwl_update_stats(priv, true, fc, len); tx->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; tx->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; @@ -712,8 +650,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) len, PCI_DMA_TODEVICE); /* we do not map meta data ... so we can safely access address to * provide to unmap command*/ - pci_unmap_addr_set(&out_cmd->meta, mapping, txcmd_phys); - pci_unmap_len_set(&out_cmd->meta, len, len); + pci_unmap_addr_set(out_meta, mapping, txcmd_phys); + pci_unmap_len_set(out_meta, len, len); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ @@ -823,7 +761,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_SPECTRUM_MEASUREMENT_CMD, .data = (void *)&spectrum, - .meta.flags = CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; u32 add_time = le64_to_cpu(params->start_time); int rc; @@ -864,7 +802,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, if (rc) return rc; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_RX_ON_ASSOC command\n"); rc = -EIO; @@ -887,7 +825,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, break; } - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return rc; } @@ -996,7 +934,7 @@ static void iwl3945_rx_card_state_notif(struct iwl_priv *priv, u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = priv->status; - IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n", + IWL_WARN(priv, "Card state received: HW:%s SW:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", (flags & SW_CARD_DISABLED) ? "Kill" : "On"); @@ -1714,7 +1652,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { + if (iwl_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", @@ -1733,7 +1671,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(priv, "Microcode HW error detected. Restarting.\n"); + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(priv); @@ -1749,7 +1687,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1828,7 +1766,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) iwl_enable_interrupts(priv); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { inta = iwl_read32(priv, CSR_INT); inta_mask = iwl_read32(priv, CSR_INT_MASK); inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); @@ -1844,7 +1782,7 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, u8 is_active, u8 n_probes, struct iwl3945_scan_channel *scan_ch) { - const struct ieee80211_channel *channels = NULL; + struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; const struct iwl_channel_info *ch_info; u16 passive_dwell = 0; @@ -1855,19 +1793,19 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, if (!sband) return 0; - channels = sband->channels; - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); passive_dwell = iwl_get_passive_dwell_time(priv, band); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; - for (i = 0, added = 0; i < sband->n_channels; i++) { - if (channels[i].flags & IEEE80211_CHAN_DISABLED) + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) continue; - scan_ch->channel = channels[i].hw_value; + scan_ch->channel = chan->hw_value; ch_info = iwl_get_channel_info(priv, band, scan_ch->channel); if (!is_channel_valid(ch_info)) { @@ -1882,7 +1820,7 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, * and use long active_dwell time. */ if (!is_active || is_channel_passive(ch_info) || - (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) { + (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) { scan_ch->type = 0; /* passive */ if (IWL_UCODE_API(priv->ucode_ver) == 1) scan_ch->active_dwell = cpu_to_le16(passive_dwell - 1); @@ -2111,7 +2049,7 @@ static void iwl3945_nic_start(struct iwl_priv *priv) */ static int iwl3945_read_ucode(struct iwl_priv *priv) { - struct iwl_ucode *ucode; + const struct iwl_ucode_header *ucode; int ret = -EINVAL, index; const struct firmware *ucode_raw; /* firmware file name contains uCode/driver compatibility version */ @@ -2152,22 +2090,24 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) goto error; /* Make sure that we got at least our header! */ - if (ucode_raw->size < sizeof(*ucode)) { + if (ucode_raw->size < priv->cfg->ops->ucode->get_header_size(1)) { IWL_ERR(priv, "File size way too small!\n"); ret = -EINVAL; goto err_release; } /* Data from ucode file: header followed by uCode images */ - ucode = (void *)ucode_raw->data; + ucode = (struct iwl_ucode_header *)ucode_raw->data; priv->ucode_ver = le32_to_cpu(ucode->ver); api_ver = IWL_UCODE_API(priv->ucode_ver); - inst_size = le32_to_cpu(ucode->inst_size); - data_size = le32_to_cpu(ucode->data_size); - init_size = le32_to_cpu(ucode->init_size); - init_data_size = le32_to_cpu(ucode->init_data_size); - boot_size = le32_to_cpu(ucode->boot_size); + inst_size = priv->cfg->ops->ucode->get_inst_size(ucode, api_ver); + data_size = priv->cfg->ops->ucode->get_data_size(ucode, api_ver); + init_size = priv->cfg->ops->ucode->get_init_size(ucode, api_ver); + init_data_size = + priv->cfg->ops->ucode->get_init_data_size(ucode, api_ver); + boot_size = priv->cfg->ops->ucode->get_boot_size(ucode, api_ver); + src = priv->cfg->ops->ucode->get_data(ucode, api_ver); /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely @@ -2208,12 +2148,13 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size < sizeof(*ucode) + + if (ucode_raw->size != priv->cfg->ops->ucode->get_header_size(api_ver) + inst_size + data_size + init_size + init_data_size + boot_size) { - IWL_DEBUG_INFO(priv, "uCode file size %zd too small\n", - ucode_raw->size); + IWL_DEBUG_INFO(priv, + "uCode file size %zd does not match expected size\n", + ucode_raw->size); ret = -EINVAL; goto err_release; } @@ -2296,44 +2237,44 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* Copy images into buffers for card's bus-master reads ... */ /* Runtime instructions (first block of data in file) */ - src = &ucode->data[0]; - len = priv->ucode_code.len; + len = inst_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %zd\n", len); memcpy(priv->ucode_code.v_addr, src, len); + src += len; + IWL_DEBUG_INFO(priv, "uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr); /* Runtime data (2nd block) * NOTE: Copy into backup buffer will be done in iwl3945_up() */ - src = &ucode->data[inst_size]; - len = priv->ucode_data.len; + len = data_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode data len %zd\n", len); memcpy(priv->ucode_data.v_addr, src, len); memcpy(priv->ucode_data_backup.v_addr, src, len); + src += len; /* Initialization instructions (3rd block) */ if (init_size) { - src = &ucode->data[inst_size + data_size]; - len = priv->ucode_init.len; + len = init_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init instr len %zd\n", len); memcpy(priv->ucode_init.v_addr, src, len); + src += len; } /* Initialization data (4th block) */ if (init_data_size) { - src = &ucode->data[inst_size + data_size + init_size]; - len = priv->ucode_init_data.len; + len = init_data_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init data len %zd\n", len); memcpy(priv->ucode_init_data.v_addr, src, len); + src += len; } /* Bootstrap instructions (5th block) */ - src = &ucode->data[inst_size + data_size + init_size + init_data_size]; - len = priv->ucode_boot.len; + len = boot_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %zd\n", len); memcpy(priv->ucode_boot.v_addr, src, len); @@ -2784,7 +2725,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data) struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = sizeof(struct iwl3945_scan_cmd), - .meta.flags = CMD_SIZE_HUGE, + .flags = CMD_SIZE_HUGE, }; int rc = 0; struct iwl3945_scan_cmd *scan; @@ -3066,7 +3007,7 @@ void iwl3945_post_associate(struct iwl_priv *priv) iwlcore_commit_rxon(priv); memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); - iwl3945_setup_rxon_timing(priv); + iwl_setup_rxon_timing(priv); rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, sizeof(priv->rxon_timing), &priv->rxon_timing); if (rc) @@ -3261,7 +3202,7 @@ void iwl3945_config_ap(struct iwl_priv *priv) /* RXON Timing */ memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); - iwl3945_setup_rxon_timing(priv); + iwl_setup_rxon_timing(priv); rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, sizeof(priv->rxon_timing), &priv->rxon_timing); @@ -3375,13 +3316,16 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, * used for controlling the debug level. * * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. */ static ssize_t show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - - return sprintf(buf, "0x%08X\n", priv->debug_level); + return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); } static ssize_t store_debug_level(struct device *d, struct device_attribute *attr, @@ -3394,9 +3338,12 @@ static ssize_t store_debug_level(struct device *d, ret = strict_strtoul(buf, 0, &val); if (ret) IWL_INFO(priv, "%s is not in hex or decimal form.\n", buf); - else + else { priv->debug_level = val; - + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, + "Not enough memory to generate traffic log\n"); + } return strnlen(buf, count); } @@ -3612,65 +3559,6 @@ static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, show_retry_rate, store_retry_rate); -static ssize_t store_power_level(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - int ret; - unsigned long mode; - - - mutex_lock(&priv->mutex); - - ret = strict_strtoul(buf, 10, &mode); - if (ret) - goto out; - - ret = iwl_power_set_user_mode(priv, mode); - if (ret) { - IWL_DEBUG_MAC80211(priv, "failed setting power mode.\n"); - goto out; - } - ret = count; - - out: - mutex_unlock(&priv->mutex); - return ret; -} - -static ssize_t show_power_level(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - int level = priv->power_data.power_mode; - char *p = buf; - - p += sprintf(p, "%d\n", level); - return p - buf + 1; -} - -static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, - show_power_level, store_power_level); - -#define MAX_WX_STRING 80 - -/* Values are in microsecond */ -static const s32 timeout_duration[] = { - 350000, - 250000, - 75000, - 37000, - 25000, -}; -static const s32 period_duration[] = { - 400000, - 700000, - 1000000, - 1000000, - 1000000 -}; - static ssize_t show_channels(struct device *d, struct device_attribute *attr, char *buf) { @@ -3847,7 +3735,6 @@ static struct attribute *iwl3945_sysfs_entries[] = { #ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT &dev_attr_measurement.attr, #endif - &dev_attr_power_level.attr, &dev_attr_retry_rate.attr, &dev_attr_statistics.attr, &dev_attr_status.attr, @@ -3912,8 +3799,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) priv->qos_data.qos_cap.val = 0; priv->rates_mask = IWL_RATES_MASK; - /* If power management is turned on, default to CAM mode */ - priv->power_mode = IWL_POWER_MODE_CAM; priv->tx_power_user_lmt = IWL_DEFAULT_TX_POWER; if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { @@ -3960,7 +3845,9 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | - IEEE80211_HW_SPECTRUM_MGMT; + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | @@ -4020,15 +3907,6 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e priv = hw->priv; SET_IEEE80211_DEV(hw, &pdev->dev); - if ((iwl3945_mod_params.num_of_queues > IWL39_MAX_NUM_QUEUES) || - (iwl3945_mod_params.num_of_queues < IWL39_MIN_NUM_QUEUES)) { - IWL_ERR(priv, - "invalid queues_num, should be between %d and %d\n", - IWL39_MIN_NUM_QUEUES, IWL39_MAX_NUM_QUEUES); - err = -EINVAL; - goto out_ieee80211_free_hw; - } - /* * Disabling hardware scan means that mac80211 will perform scans * "the hard way", rather than using device's scan. @@ -4045,9 +3923,10 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e priv->inta_mask = CSR_INI_SET_MASK; #ifdef CONFIG_IWLWIFI_DEBUG - priv->debug_level = iwl3945_mod_params.debug; atomic_set(&priv->restrict_refcnt, 0); #endif + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, "Not enough memory to generate traffic log\n"); /*************************** * 2. Initializing PCI bus @@ -4210,6 +4089,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(priv->hw); + iwl_free_traffic_mem(priv); out: return err; } @@ -4265,6 +4145,7 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) * until now... */ destroy_workqueue(priv->workqueue); priv->workqueue = NULL; + iwl_free_traffic_mem(priv); free_irq(pdev->irq, priv); pci_disable_msi(pdev); @@ -4341,14 +4222,12 @@ MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, 0444); MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])\n"); -module_param_named(debug, iwl3945_mod_params.debug, uint, 0444); +#ifdef CONFIG_IWLWIFI_DEBUG +module_param_named(debug, iwl_debug_level, uint, 0644); MODULE_PARM_DESC(debug, "debug output mask"); +#endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, 0444); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); - -module_param_named(queues_num, iwl3945_mod_params.num_of_queues, int, 0444); -MODULE_PARM_DESC(queues_num, "number of hw queues."); - module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, 0444); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); |