diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-lib.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 320 | 
1 files changed, 168 insertions, 152 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 0f292a210ed..a1b6d202d57 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -77,7 +77,7 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,  		IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",  				   agg->frame_count, agg->start_idx, idx); -		info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]); +		info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb);  		info->status.rates[0].count = tx_resp->failure_frame + 1;  		info->flags &= ~IEEE80211_TX_CTL_AMPDU;  		info->flags |= iwl_tx_status_to_mac80211(status); @@ -93,6 +93,12 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,  	} else {  		/* Two or more frames were attempted; expect block-ack */  		u64 bitmap = 0; + +		/* +		 * Start is the lowest frame sent. It may not be the first +		 * frame in the batch; we figure this out dynamically during +		 * the following loop. +		 */  		int start = agg->start_idx;  		/* Construct bit-map of pending frames within Tx window */ @@ -131,25 +137,58 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,  			IWL_DEBUG_TX_REPLY(priv, "AGG Frame i=%d idx %d seq=%d\n",  					   i, idx, SEQ_TO_SN(sc)); +			/* +			 * sh -> how many frames ahead of the starting frame is +			 * the current one? +			 * +			 * Note that all frames sent in the batch must be in a +			 * 64-frame window, so this number should be in [0,63]. +			 * If outside of this window, then we've found a new +			 * "first" frame in the batch and need to change start. +			 */  			sh = idx - start; -			if (sh > 64) { -				sh = (start - idx) + 0xff; + +			/* +			 * If >= 64, out of window. start must be at the front +			 * of the circular buffer, idx must be near the end of +			 * the buffer, and idx is the new "first" frame. Shift +			 * the indices around. +			 */ +			if (sh >= 64) { +				/* Shift bitmap by start - idx, wrapped */ +				sh = 0x100 - idx + start;  				bitmap = bitmap << sh; +				/* Now idx is the new start so sh = 0 */  				sh = 0;  				start = idx; -			} else if (sh < -64) -				sh  = 0xff - (start - idx); -			else if (sh < 0) { +			/* +			 * If <= -64 then wraps the 256-pkt circular buffer +			 * (e.g., start = 255 and idx = 0, sh should be 1) +			 */ +			} else if (sh <= -64) { +				sh  = 0x100 - start + idx; +			/* +			 * If < 0 but > -64, out of window. idx is before start +			 * but not wrapped. Shift the indices around. +			 */ +			} else if (sh < 0) { +				/* Shift by how far start is ahead of idx */  				sh = start - idx; -				start = idx;  				bitmap = bitmap << sh; +				/* Now idx is the new start so sh = 0 */ +				start = idx;  				sh = 0;  			} +			/* Sequence number start + sh was sent in this batch */  			bitmap |= 1ULL << sh;  			IWL_DEBUG_TX_REPLY(priv, "start=%d bitmap=0x%llx\n",  					   start, (unsigned long long)bitmap);  		} +		/* +		 * Store the bitmap and possibly the new start, if we wrapped +		 * the buffer above +		 */  		agg->bitmap = bitmap;  		agg->start_idx = start;  		IWL_DEBUG_TX_REPLY(priv, "Frames %d start_idx=%d bitmap=0x%llx\n", @@ -166,7 +205,9 @@ void iwl_check_abort_status(struct iwl_priv *priv,  			    u8 frame_count, u32 status)  {  	if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { -		IWL_ERR(priv, "TODO: Implement Tx flush command!!!\n"); +		IWL_ERR(priv, "Tx flush command to flush out all frames\n"); +		if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) +			queue_work(priv->workqueue, &priv->tx_flush);  	}  } @@ -184,6 +225,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,  	int tid;  	int sta_id;  	int freed; +	unsigned long flags;  	if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) {  		IWL_ERR(priv, "Read index for DMA queue txq_id (%d) index %d " @@ -193,15 +235,16 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,  		return;  	} -	info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb[0]); +	info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb);  	memset(&info->status, 0, sizeof(info->status));  	tid = (tx_resp->ra_tid & IWL50_TX_RES_TID_MSK) >> IWL50_TX_RES_TID_POS;  	sta_id = (tx_resp->ra_tid & IWL50_TX_RES_RA_MSK) >> IWL50_TX_RES_RA_POS; +	spin_lock_irqsave(&priv->sta_lock, flags);  	if (txq->sched_retry) {  		const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp); -		struct iwl_ht_agg *agg = NULL; +		struct iwl_ht_agg *agg;  		agg = &priv->stations[sta_id].tid[tid].agg; @@ -256,6 +299,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,  	iwlagn_txq_check_empty(priv, sta_id, tid, txq_id);  	iwl_check_abort_status(priv, tx_resp->frame_count, status); +	spin_unlock_irqrestore(&priv->sta_lock, flags);  }  void iwlagn_rx_handler_setup(struct iwl_priv *priv) @@ -319,7 +363,8 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)  void iwlagn_temperature(struct iwl_priv *priv)  {  	/* store temperature from statistics (in Celsius) */ -	priv->temperature = le32_to_cpu(priv->statistics.general.temperature); +	priv->temperature = +		le32_to_cpu(priv->_agn.statistics.general.common.temperature);  	iwl_tt_handler(priv);  } @@ -444,7 +489,7 @@ int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)  	/* Tell device where to find RBD circular buffer in DRAM */  	iwl_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG, -			   (u32)(rxq->dma_addr >> 8)); +			   (u32)(rxq->bd_dma >> 8));  	/* Tell device where in DRAM to update its Rx status */  	iwl_write_direct32(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG, @@ -709,7 +754,7 @@ void iwlagn_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)  	}  	dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, -			  rxq->dma_addr); +			  rxq->bd_dma);  	dma_free_coherent(&priv->pci_dev->dev, sizeof(struct iwl_rb_status),  			  rxq->rb_stts, rxq->rb_stts_dma);  	rxq->bd = NULL; @@ -755,132 +800,6 @@ static inline int iwlagn_calc_rssi(struct iwl_priv *priv,  	return priv->cfg->ops->utils->calc_rssi(priv, rx_resp);  } -#ifdef CONFIG_IWLWIFI_DEBUG -/** - * iwlagn_dbg_report_frame - dump frame to syslog during debug sessions - * - * You may hack this function to show different aspects of received frames, - * including selective frame dumps. - * group100 parameter selects whether to show 1 out of 100 good data frames. - *    All beacon and probe response frames are printed. - */ -static void iwlagn_dbg_report_frame(struct iwl_priv *priv, -		      struct iwl_rx_phy_res *phy_res, u16 length, -		      struct ieee80211_hdr *header, int group100) -{ -	u32 to_us; -	u32 print_summary = 0; -	u32 print_dump = 0;	/* set to 1 to dump all frames' contents */ -	u32 hundred = 0; -	u32 dataframe = 0; -	__le16 fc; -	u16 seq_ctl; -	u16 channel; -	u16 phy_flags; -	u32 rate_n_flags; -	u32 tsf_low; -	int rssi; - -	if (likely(!(iwl_get_debug_level(priv) & IWL_DL_RX))) -		return; - -	/* MAC header */ -	fc = header->frame_control; -	seq_ctl = le16_to_cpu(header->seq_ctrl); - -	/* metadata */ -	channel = le16_to_cpu(phy_res->channel); -	phy_flags = le16_to_cpu(phy_res->phy_flags); -	rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); - -	/* signal statistics */ -	rssi = iwlagn_calc_rssi(priv, phy_res); -	tsf_low = le64_to_cpu(phy_res->timestamp) & 0x0ffffffff; - -	to_us = !compare_ether_addr(header->addr1, priv->mac_addr); - -	/* if data frame is to us and all is good, -	 *   (optionally) print summary for only 1 out of every 100 */ -	if (to_us && (fc & ~cpu_to_le16(IEEE80211_FCTL_PROTECTED)) == -	    cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { -		dataframe = 1; -		if (!group100) -			print_summary = 1;	/* print each frame */ -		else if (priv->framecnt_to_us < 100) { -			priv->framecnt_to_us++; -			print_summary = 0; -		} else { -			priv->framecnt_to_us = 0; -			print_summary = 1; -			hundred = 1; -		} -	} else { -		/* print summary for all other frames */ -		print_summary = 1; -	} - -	if (print_summary) { -		char *title; -		int rate_idx; -		u32 bitrate; - -		if (hundred) -			title = "100Frames"; -		else if (ieee80211_has_retry(fc)) -			title = "Retry"; -		else if (ieee80211_is_assoc_resp(fc)) -			title = "AscRsp"; -		else if (ieee80211_is_reassoc_resp(fc)) -			title = "RasRsp"; -		else if (ieee80211_is_probe_resp(fc)) { -			title = "PrbRsp"; -			print_dump = 1;	/* dump frame contents */ -		} else if (ieee80211_is_beacon(fc)) { -			title = "Beacon"; -			print_dump = 1;	/* dump frame contents */ -		} else if (ieee80211_is_atim(fc)) -			title = "ATIM"; -		else if (ieee80211_is_auth(fc)) -			title = "Auth"; -		else if (ieee80211_is_deauth(fc)) -			title = "DeAuth"; -		else if (ieee80211_is_disassoc(fc)) -			title = "DisAssoc"; -		else -			title = "Frame"; - -		rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); -		if (unlikely((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT))) { -			bitrate = 0; -			WARN_ON_ONCE(1); -		} else { -			bitrate = iwl_rates[rate_idx].ieee / 2; -		} - -		/* print frame summary. -		 * MAC addresses show just the last byte (for brevity), -		 *    but you can hack it to show more, if you'd like to. */ -		if (dataframe) -			IWL_DEBUG_RX(priv, "%s: mhd=0x%04x, dst=0x%02x, " -				     "len=%u, rssi=%d, chnl=%d, rate=%u,\n", -				     title, le16_to_cpu(fc), header->addr1[5], -				     length, rssi, channel, bitrate); -		else { -			/* src/dst addresses assume managed mode */ -			IWL_DEBUG_RX(priv, "%s: 0x%04x, dst=0x%02x, src=0x%02x, " -				     "len=%u, rssi=%d, tim=%lu usec, " -				     "phy=0x%02x, chnl=%d\n", -				     title, le16_to_cpu(fc), header->addr1[5], -				     header->addr3[5], length, rssi, -				     tsf_low - priv->scan_start_tsf, -				     phy_flags, channel); -		} -	} -	if (print_dump) -		iwl_print_hex_dump(priv, IWL_DL_RX, header, length); -} -#endif -  static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)  {  	u32 decrypt_out = 0; @@ -988,7 +907,7 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv,  	struct iwl_rx_packet *pkt = rxb_addr(rxb);  	struct iwl_rx_phy_res *phy_res;  	__le32 rx_pkt_status; -	struct iwl4965_rx_mpdu_res_start *amsdu; +	struct iwl_rx_mpdu_res_start *amsdu;  	u32 len;  	u32 ampdu_status;  	u32 rate_n_flags; @@ -1017,7 +936,7 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv,  			return;  		}  		phy_res = &priv->_agn.last_phy_res; -		amsdu = (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; +		amsdu = (struct iwl_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); @@ -1060,11 +979,6 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv,  	/* Find max signal strength (dBm) among 3 antenna/receiver chains */  	rx_status.signal = iwlagn_calc_rssi(priv, phy_res); -#ifdef CONFIG_IWLWIFI_DEBUG -	/* Set "1" to report good data frames in groups of 100 */ -	if (unlikely(iwl_get_debug_level(priv) & IWL_DL_RX)) -		iwlagn_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, TSF %llu\n",  		rx_status.signal, (unsigned long long)rx_status.mactime); @@ -1252,6 +1166,7 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)  	bool is_active = false;  	int  chan_mod;  	u8 active_chains; +	u8 scan_tx_antennas = priv->hw_params.valid_tx_ant;  	conf = ieee80211_get_hw_conf(priv->hw); @@ -1319,7 +1234,10 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)  		IWL_DEBUG_INFO(priv, "Scanning while associated...\n");  		spin_lock_irqsave(&priv->lock, flags); -		interval = vif ? vif->bss_conf.beacon_int : 0; +		if (priv->is_internal_short_scan) +			interval = 0; +		else +			interval = vif->bss_conf.beacon_int;  		spin_unlock_irqrestore(&priv->lock, flags);  		scan->suspend_time = 0; @@ -1403,11 +1321,14 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)  	band = priv->scan_band; -	if (priv->cfg->scan_antennas[band]) -		rx_ant = priv->cfg->scan_antennas[band]; +	if (priv->cfg->scan_rx_antennas[band]) +		rx_ant = priv->cfg->scan_rx_antennas[band]; + +	if (priv->cfg->scan_tx_antennas[band]) +		scan_tx_antennas = priv->cfg->scan_tx_antennas[band]; -	priv->scan_tx_ant[band] = -			iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band]); +	priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band], +						    scan_tx_antennas);  	rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);  	scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); @@ -1433,13 +1354,15 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)  	if (!priv->is_internal_short_scan) {  		cmd_len = iwl_fill_probe_req(priv,  					(struct ieee80211_mgmt *)scan->data, +					vif->addr,  					priv->scan_request->ie,  					priv->scan_request->ie_len,  					IWL_MAX_SCAN_SIZE - sizeof(*scan));  	} else { +		/* use bcast addr, will not be transmitted but must be valid */  		cmd_len = iwl_fill_probe_req(priv,  					(struct ieee80211_mgmt *)scan->data, -					NULL, 0, +					iwl_bcast_addr, NULL, 0,  					IWL_MAX_SCAN_SIZE - sizeof(*scan));  	} @@ -1502,3 +1425,96 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv,  	return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id,  				  vif->bss_conf.bssid);  } + +void iwl_free_tfds_in_queue(struct iwl_priv *priv, +			    int sta_id, int tid, int freed) +{ +	WARN_ON(!spin_is_locked(&priv->sta_lock)); + +	if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed) +		priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; +	else { +		IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n", +			priv->stations[sta_id].tid[tid].tfds_in_queue, +			freed); +		priv->stations[sta_id].tid[tid].tfds_in_queue = 0; +	} +} + +#define IWL_FLUSH_WAIT_MS	2000 + +int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv) +{ +	struct iwl_tx_queue *txq; +	struct iwl_queue *q; +	int cnt; +	unsigned long now = jiffies; +	int ret = 0; + +	/* waiting for all the tx frames complete might take a while */ +	for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { +		if (cnt == IWL_CMD_QUEUE_NUM) +			continue; +		txq = &priv->txq[cnt]; +		q = &txq->q; +		while (q->read_ptr != q->write_ptr && !time_after(jiffies, +		       now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) +				msleep(1); + +		if (q->read_ptr != q->write_ptr) { +			IWL_ERR(priv, "fail to flush all tx fifo queues\n"); +			ret = -ETIMEDOUT; +			break; +		} +	} +	return ret; +} + +#define IWL_TX_QUEUE_MSK	0xfffff + +/** + * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode + * + * pre-requirements: + *  1. acquire mutex before calling + *  2. make sure rf is on and not in exit state + */ +int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +{ +	struct iwl_txfifo_flush_cmd flush_cmd; +	struct iwl_host_cmd cmd = { +		.id = REPLY_TXFIFO_FLUSH, +		.len = sizeof(struct iwl_txfifo_flush_cmd), +		.flags = CMD_SYNC, +		.data = &flush_cmd, +	}; + +	might_sleep(); + +	memset(&flush_cmd, 0, sizeof(flush_cmd)); +	flush_cmd.fifo_control = IWL_TX_FIFO_VO_MSK | IWL_TX_FIFO_VI_MSK | +				 IWL_TX_FIFO_BE_MSK | IWL_TX_FIFO_BK_MSK; +	if (priv->cfg->sku & IWL_SKU_N) +		flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; + +	IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", +		       flush_cmd.fifo_control); +	flush_cmd.flush_control = cpu_to_le16(flush_control); + +	return iwl_send_cmd(priv, &cmd); +} + +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +{ +	mutex_lock(&priv->mutex); +	ieee80211_stop_queues(priv->hw); +	if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) { +		IWL_ERR(priv, "flush request fail\n"); +		goto done; +	} +	IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); +	iwlagn_wait_tx_queue_empty(priv); +done: +	ieee80211_wake_queues(priv->hw); +	mutex_unlock(&priv->mutex); +}  |