Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
		
						commit
						4ba3eb034f
					
				| @ -850,6 +850,7 @@ static int ar9170_rx_mac_status(struct ar9170 *ar, | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case AR9170_RX_STATUS_MODULATION_DUPOFDM: | ||||
| 	case AR9170_RX_STATUS_MODULATION_OFDM: | ||||
| 		switch (head->plcp[0] & 0xf) { | ||||
| 		case 0xb: | ||||
| @ -897,8 +898,7 @@ static int ar9170_rx_mac_status(struct ar9170 *ar, | ||||
| 		status->flag |= RX_FLAG_HT; | ||||
| 		break; | ||||
| 
 | ||||
| 	case AR9170_RX_STATUS_MODULATION_DUPOFDM: | ||||
| 		/* XXX */ | ||||
| 	default: | ||||
| 		if (ar9170_nag_limiter(ar)) | ||||
| 			printk(KERN_ERR "%s: invalid modulation\n", | ||||
| 			       wiphy_name(ar->hw->wiphy)); | ||||
| @ -2441,6 +2441,7 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, | ||||
| } | ||||
| 
 | ||||
| static int ar9170_ampdu_action(struct ieee80211_hw *hw, | ||||
| 			       struct ieee80211_vif *vif, | ||||
| 			       enum ieee80211_ampdu_mlme_action action, | ||||
| 			       struct ieee80211_sta *sta, u16 tid, u16 *ssn) | ||||
| { | ||||
| @ -2470,7 +2471,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw, | ||||
| 		tid_info->state = AR9170_TID_STATE_PROGRESS; | ||||
| 		tid_info->active = false; | ||||
| 		spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); | ||||
| 		ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); | ||||
| 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); | ||||
| 		break; | ||||
| 
 | ||||
| 	case IEEE80211_AMPDU_TX_STOP: | ||||
| @ -2480,7 +2481,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw, | ||||
| 		tid_info->active = false; | ||||
| 		skb_queue_purge(&tid_info->queue); | ||||
| 		spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); | ||||
| 		ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); | ||||
| 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); | ||||
| 		break; | ||||
| 
 | ||||
| 	case IEEE80211_AMPDU_TX_OPERATIONAL: | ||||
|  | ||||
| @ -68,8 +68,10 @@ static struct usb_device_id ar9170_usb_ids[] = { | ||||
| 	{ USB_DEVICE(0x0cf3, 0x1002) }, | ||||
| 	/* Cace Airpcap NX */ | ||||
| 	{ USB_DEVICE(0xcace, 0x0300) }, | ||||
| 	/* D-Link DWA 160A */ | ||||
| 	/* D-Link DWA 160 A1 */ | ||||
| 	{ USB_DEVICE(0x07d1, 0x3c10) }, | ||||
| 	/* D-Link DWA 160 A2 */ | ||||
| 	{ USB_DEVICE(0x07d1, 0x3a09) }, | ||||
| 	/* Netgear WNDA3100 */ | ||||
| 	{ USB_DEVICE(0x0846, 0x9010) }, | ||||
| 	/* Netgear WN111 v2 */ | ||||
|  | ||||
| @ -1399,7 +1399,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, | ||||
| 	if (i_coffd == 0 || q_coffd == 0) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	i_coff = ((-iq_corr) / i_coffd) & 0x3f; | ||||
| 	i_coff = ((-iq_corr) / i_coffd); | ||||
| 
 | ||||
| 	/* Boundary check */ | ||||
| 	if (i_coff > 31) | ||||
| @ -1407,7 +1407,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, | ||||
| 	if (i_coff < -32) | ||||
| 		i_coff = -32; | ||||
| 
 | ||||
| 	q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f; | ||||
| 	q_coff = (((s32)i_pwr / q_coffd) - 128); | ||||
| 
 | ||||
| 	/* Boundary check */ | ||||
| 	if (q_coff > 15) | ||||
|  | ||||
| @ -198,18 +198,8 @@ struct ath_txq { | ||||
| 	struct list_head axq_q; | ||||
| 	spinlock_t axq_lock; | ||||
| 	u32 axq_depth; | ||||
| 	u8 axq_aggr_depth; | ||||
| 	bool stopped; | ||||
| 	bool axq_tx_inprogress; | ||||
| 	struct ath_buf *axq_linkbuf; | ||||
| 
 | ||||
| 	/* first desc of the last descriptor that contains CTS */ | ||||
| 	struct ath_desc *axq_lastdsWithCTS; | ||||
| 
 | ||||
| 	/* final desc of the gating desc that determines whether
 | ||||
| 	   lastdsWithCTS has been DMA'ed or not */ | ||||
| 	struct ath_desc *axq_gatingds; | ||||
| 
 | ||||
| 	struct list_head axq_acq; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -231,26 +231,35 @@ void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, | ||||
| { | ||||
| 	struct ath_hw *ah = common->ah; | ||||
| 	struct ieee80211_hdr *hdr; | ||||
| 	int hdrlen, padsize; | ||||
| 	int hdrlen, padpos, padsize; | ||||
| 	u8 keyix; | ||||
| 	__le16 fc; | ||||
| 
 | ||||
| 	/* see if any padding is done by the hw and remove it */ | ||||
| 	hdr = (struct ieee80211_hdr *) skb->data; | ||||
| 	hdrlen = ieee80211_get_hdrlen_from_skb(skb); | ||||
| 	padpos = 24; | ||||
| 	fc = hdr->frame_control; | ||||
| 	if ((fc & cpu_to_le16(IEEE80211_FCTL_FROMDS|IEEE80211_FCTL_TODS)) == | ||||
| 	    cpu_to_le16(IEEE80211_FCTL_FROMDS|IEEE80211_FCTL_TODS)) { | ||||
| 	  padpos += 6; /* ETH_ALEN */ | ||||
| 	} | ||||
| 	if ((fc & cpu_to_le16(IEEE80211_STYPE_QOS_DATA|IEEE80211_FCTL_FTYPE)) == | ||||
| 	    cpu_to_le16(IEEE80211_STYPE_QOS_DATA|IEEE80211_FTYPE_DATA)) { | ||||
| 	  padpos += 2; | ||||
| 	} | ||||
| 
 | ||||
| 	/* The MAC header is padded to have 32-bit boundary if the
 | ||||
| 	 * packet payload is non-zero. The general calculation for | ||||
| 	 * padsize would take into account odd header lengths: | ||||
| 	 * padsize = (4 - hdrlen % 4) % 4; However, since only | ||||
| 	 * padsize = (4 - padpos % 4) % 4; However, since only | ||||
| 	 * even-length headers are used, padding can only be 0 or 2 | ||||
| 	 * bytes and we can optimize this a bit. In addition, we must | ||||
| 	 * not try to remove padding from short control frames that do | ||||
| 	 * not have payload. */ | ||||
| 	padsize = hdrlen & 3; | ||||
| 	if (padsize && hdrlen >= 24) { | ||||
| 		memmove(skb->data + padsize, skb->data, hdrlen); | ||||
| 	padsize = padpos & 3; | ||||
| 	if (padsize && skb->len>=padpos+padsize+FCS_LEN) { | ||||
| 		memmove(skb->data + padsize, skb->data, padpos); | ||||
| 		skb_pull(skb, padsize); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -81,6 +81,7 @@ struct ath_buf { | ||||
| 	u16 bf_flags; | ||||
| 	struct ath_buf_state bf_state; | ||||
| 	dma_addr_t bf_dmacontext; | ||||
| 	struct ath_wiphy *aphy; | ||||
| }; | ||||
| 
 | ||||
| struct ath_atx_tid { | ||||
|  | ||||
| @ -257,14 +257,17 @@ static const struct file_operations fops_interrupt = { | ||||
| 
 | ||||
| void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb) | ||||
| { | ||||
| 	struct ath_tx_info_priv *tx_info_priv = NULL; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_tx_rate *rates = tx_info->status.rates; | ||||
| 	int final_ts_idx, idx; | ||||
| 	int final_ts_idx = 0, idx, i; | ||||
| 	struct ath_rc_stats *stats; | ||||
| 
 | ||||
| 	tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 	final_ts_idx = tx_info_priv->tx.ts_rateindex; | ||||
| 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { | ||||
| 		if (!rates[i].count) | ||||
| 			break; | ||||
| 
 | ||||
| 		final_ts_idx = i; | ||||
| 	} | ||||
| 	idx = rates[final_ts_idx].idx; | ||||
| 	stats = &sc->debug.stats.rcstats[idx]; | ||||
| 	stats->success++; | ||||
|  | ||||
| @ -390,8 +390,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah) | ||||
| 	ah->config.cck_trig_high = 200; | ||||
| 	ah->config.cck_trig_low = 100; | ||||
| 	ah->config.enable_ani = 1; | ||||
| 	ah->config.diversity_control = ATH9K_ANT_VARIABLE; | ||||
| 	ah->config.antenna_switch_swap = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { | ||||
| 		ah->config.spurchans[i][0] = AR_NO_SPUR; | ||||
| @ -446,9 +444,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) | ||||
| 	ah->acktimeout = (u32) -1; | ||||
| 	ah->ctstimeout = (u32) -1; | ||||
| 	ah->globaltxtimeout = (u32) -1; | ||||
| 
 | ||||
| 	ah->gbeacon_rate = 0; | ||||
| 
 | ||||
| 	ah->power_mode = ATH9K_PM_UNDEFINED; | ||||
| } | ||||
| 
 | ||||
| @ -1151,7 +1146,7 @@ static void ath9k_hw_init_chain_masks(struct ath_hw *ah) | ||||
| 		REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, | ||||
| 			    AR_PHY_SWAP_ALT_CHAIN); | ||||
| 	case 0x3: | ||||
| 		if (((ah)->hw_version.macVersion <= AR_SREV_VERSION_9160)) { | ||||
| 		if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) { | ||||
| 			REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); | ||||
| 			REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); | ||||
| 			break; | ||||
| @ -2056,9 +2051,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, | ||||
| 	ah->ath9k_hw_spur_mitigate_freq(ah, chan); | ||||
| 	ah->eep_ops->set_board_values(ah, chan); | ||||
| 
 | ||||
| 	if (AR_SREV_5416(ah)) | ||||
| 		ath9k_hw_decrease_chain_power(ah, chan); | ||||
| 
 | ||||
| 	REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr)); | ||||
| 	REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4) | ||||
| 		  | macStaId1 | ||||
| @ -3518,51 +3510,6 @@ void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna) | ||||
| } | ||||
| EXPORT_SYMBOL(ath9k_hw_setantenna); | ||||
| 
 | ||||
| bool ath9k_hw_setantennaswitch(struct ath_hw *ah, | ||||
| 			       enum ath9k_ant_setting settings, | ||||
| 			       struct ath9k_channel *chan, | ||||
| 			       u8 *tx_chainmask, | ||||
| 			       u8 *rx_chainmask, | ||||
| 			       u8 *antenna_cfgd) | ||||
| { | ||||
| 	static u8 tx_chainmask_cfg, rx_chainmask_cfg; | ||||
| 
 | ||||
| 	if (AR_SREV_9280(ah)) { | ||||
| 		if (!tx_chainmask_cfg) { | ||||
| 
 | ||||
| 			tx_chainmask_cfg = *tx_chainmask; | ||||
| 			rx_chainmask_cfg = *rx_chainmask; | ||||
| 		} | ||||
| 
 | ||||
| 		switch (settings) { | ||||
| 		case ATH9K_ANT_FIXED_A: | ||||
| 			*tx_chainmask = ATH9K_ANTENNA0_CHAINMASK; | ||||
| 			*rx_chainmask = ATH9K_ANTENNA0_CHAINMASK; | ||||
| 			*antenna_cfgd = true; | ||||
| 			break; | ||||
| 		case ATH9K_ANT_FIXED_B: | ||||
| 			if (ah->caps.tx_chainmask > | ||||
| 			    ATH9K_ANTENNA1_CHAINMASK) { | ||||
| 				*tx_chainmask = ATH9K_ANTENNA1_CHAINMASK; | ||||
| 			} | ||||
| 			*rx_chainmask = ATH9K_ANTENNA1_CHAINMASK; | ||||
| 			*antenna_cfgd = true; | ||||
| 			break; | ||||
| 		case ATH9K_ANT_VARIABLE: | ||||
| 			*tx_chainmask = tx_chainmask_cfg; | ||||
| 			*rx_chainmask = rx_chainmask_cfg; | ||||
| 			*antenna_cfgd = true; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} else { | ||||
| 		ah->config.diversity_control = settings; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| /*********************/ | ||||
| /* General Operation */ | ||||
| /*********************/ | ||||
|  | ||||
| @ -148,21 +148,6 @@ enum wireless_mode { | ||||
| 	ATH9K_MODE_MAX, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * ath9k_ant_setting - transmit antenna settings | ||||
|  * | ||||
|  * Configures the antenna setting to use for transmit. | ||||
|  * | ||||
|  * @ATH9K_ANT_VARIABLE: this means transmit on all active antennas | ||||
|  * @ATH9K_ANT_FIXED_A: this means transmit on the first antenna only | ||||
|  * @ATH9K_ANT_FIXED_B: this means transmit on the second antenna only | ||||
|  */ | ||||
| enum ath9k_ant_setting { | ||||
| 	ATH9K_ANT_VARIABLE = 0, | ||||
| 	ATH9K_ANT_FIXED_A, | ||||
| 	ATH9K_ANT_FIXED_B | ||||
| }; | ||||
| 
 | ||||
| enum ath9k_hw_caps { | ||||
| 	ATH9K_HW_CAP_MIC_AESCCM                 = BIT(0), | ||||
| 	ATH9K_HW_CAP_MIC_CKIP                   = BIT(1), | ||||
| @ -226,8 +211,6 @@ struct ath9k_ops_config { | ||||
| 	u32 cck_trig_high; | ||||
| 	u32 cck_trig_low; | ||||
| 	u32 enable_ani; | ||||
| 	enum ath9k_ant_setting diversity_control; | ||||
| 	u16 antenna_switch_swap; | ||||
| 	int serialize_regmode; | ||||
| 	bool intr_mitigation; | ||||
| #define SPUR_DISABLE        	0 | ||||
| @ -572,7 +555,6 @@ struct ath_hw { | ||||
| 	u32 acktimeout; | ||||
| 	u32 ctstimeout; | ||||
| 	u32 globaltxtimeout; | ||||
| 	u8 gbeacon_rate; | ||||
| 
 | ||||
| 	/* ANI */ | ||||
| 	u32 proc_phyerr; | ||||
| @ -659,11 +641,6 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, | ||||
| void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); | ||||
| u32 ath9k_hw_getdefantenna(struct ath_hw *ah); | ||||
| void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); | ||||
| bool ath9k_hw_setantennaswitch(struct ath_hw *ah, | ||||
| 			       enum ath9k_ant_setting settings, | ||||
| 			       struct ath9k_channel *chan, | ||||
| 			       u8 *tx_chainmask, u8 *rx_chainmask, | ||||
| 			       u8 *antenna_cfgd); | ||||
| 
 | ||||
| /* General Operation */ | ||||
| bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); | ||||
|  | ||||
| @ -1893,6 +1893,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) | ||||
| 		BIT(NL80211_IFTYPE_ADHOC) | | ||||
| 		BIT(NL80211_IFTYPE_MESH_POINT); | ||||
| 
 | ||||
| 	hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; | ||||
| 
 | ||||
| 	hw->queues = 4; | ||||
| 	hw->max_rates = 4; | ||||
| 	hw->channel_change_time = 5000; | ||||
| @ -2956,90 +2958,62 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 	struct ath_hw *ah = sc->sc_ah; | ||||
| 	struct ath_common *common = ath9k_hw_common(ah); | ||||
| 	struct ath_vif *avp = (void *)vif->drv_priv; | ||||
| 	u32 rfilt = 0; | ||||
| 	int error, i; | ||||
| 	int error; | ||||
| 
 | ||||
| 	mutex_lock(&sc->mutex); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * TODO: Need to decide which hw opmode to use for | ||||
| 	 *       multi-interface cases | ||||
| 	 * XXX: This belongs into add_interface! | ||||
| 	 */ | ||||
| 	if (vif->type == NL80211_IFTYPE_AP && | ||||
| 	    ah->opmode != NL80211_IFTYPE_AP) { | ||||
| 		ah->opmode = NL80211_IFTYPE_STATION; | ||||
| 		ath9k_hw_setopmode(ah); | ||||
| 		memcpy(common->curbssid, common->macaddr, ETH_ALEN); | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		/* Set BSSID */ | ||||
| 		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); | ||||
| 		memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 		common->curaid = 0; | ||||
| 		ath9k_hw_write_associd(ah); | ||||
| 		/* Request full reset to get hw opmode changed properly */ | ||||
| 		sc->sc_flags |= SC_OP_FULL_RESET; | ||||
| 
 | ||||
| 		/* Set aggregation protection mode parameters */ | ||||
| 		sc->config.ath_aggr_prot = 0; | ||||
| 
 | ||||
| 		/* Only legacy IBSS for now */ | ||||
| 		if (vif->type == NL80211_IFTYPE_ADHOC) | ||||
| 			ath_update_chainmask(sc, 0); | ||||
| 
 | ||||
| 		ath_print(common, ATH_DBG_CONFIG, | ||||
| 			  "BSSID: %pM aid: 0x%x\n", | ||||
| 			  common->curbssid, common->curaid); | ||||
| 
 | ||||
| 		/* need to reconfigure the beacon */ | ||||
| 		sc->sc_flags &= ~SC_OP_BEACONS ; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((changed & BSS_CHANGED_BSSID) && | ||||
| 	    !is_zero_ether_addr(bss_conf->bssid)) { | ||||
| 		switch (vif->type) { | ||||
| 		case NL80211_IFTYPE_STATION: | ||||
| 		case NL80211_IFTYPE_ADHOC: | ||||
| 		case NL80211_IFTYPE_MESH_POINT: | ||||
| 			/* Set BSSID */ | ||||
| 			memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); | ||||
| 			memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 			common->curaid = 0; | ||||
| 			ath9k_hw_write_associd(ah); | ||||
| 
 | ||||
| 			/* Set aggregation protection mode parameters */ | ||||
| 			sc->config.ath_aggr_prot = 0; | ||||
| 
 | ||||
| 			ath_print(common, ATH_DBG_CONFIG, | ||||
| 				  "RX filter 0x%x bssid %pM aid 0x%x\n", | ||||
| 				  rfilt, common->curbssid, common->curaid); | ||||
| 
 | ||||
| 			/* need to reconfigure the beacon */ | ||||
| 			sc->sc_flags &= ~SC_OP_BEACONS ; | ||||
| 
 | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	/* Enable transmission of beacons (AP, IBSS, MESH) */ | ||||
| 	if ((changed & BSS_CHANGED_BEACON) || | ||||
| 	    ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) { | ||||
| 		ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); | ||||
| 		error = ath_beacon_alloc(aphy, vif); | ||||
| 		if (!error) | ||||
| 			ath_beacon_config(sc, vif); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((vif->type == NL80211_IFTYPE_ADHOC) || | ||||
| 	    (vif->type == NL80211_IFTYPE_AP) || | ||||
| 	    (vif->type == NL80211_IFTYPE_MESH_POINT)) { | ||||
| 		if ((changed & BSS_CHANGED_BEACON) || | ||||
| 		    (changed & BSS_CHANGED_BEACON_ENABLED && | ||||
| 		     bss_conf->enable_beacon)) { | ||||
| 			/*
 | ||||
| 			 * Allocate and setup the beacon frame. | ||||
| 			 * | ||||
| 			 * Stop any previous beacon DMA.  This may be | ||||
| 			 * necessary, for example, when an ibss merge | ||||
| 			 * causes reconfiguration; we may be called | ||||
| 			 * with beacon transmission active. | ||||
| 			 */ | ||||
| 	/* Disable transmission of beacons */ | ||||
| 	if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) | ||||
| 		ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT) { | ||||
| 		sc->beacon_interval = bss_conf->beacon_int; | ||||
| 		/*
 | ||||
| 		 * In case of AP mode, the HW TSF has to be reset | ||||
| 		 * when the beacon interval changes. | ||||
| 		 */ | ||||
| 		if (vif->type == NL80211_IFTYPE_AP) { | ||||
| 			sc->sc_flags |= SC_OP_TSF_RESET; | ||||
| 			ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); | ||||
| 
 | ||||
| 			error = ath_beacon_alloc(aphy, vif); | ||||
| 			if (!error) | ||||
| 				ath_beacon_config(sc, vif); | ||||
| 		} else { | ||||
| 			ath_beacon_config(sc, vif); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check for WLAN_CAPABILITY_PRIVACY ? */ | ||||
| 	if ((avp->av_opmode != NL80211_IFTYPE_STATION)) { | ||||
| 		for (i = 0; i < IEEE80211_WEP_NKID; i++) | ||||
| 			if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i)) | ||||
| 				ath9k_hw_keysetmac(sc->sc_ah, | ||||
| 						   (u16)i, | ||||
| 						   common->curbssid); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Only legacy IBSS for now */ | ||||
| 	if (vif->type == NL80211_IFTYPE_ADHOC) | ||||
| 		ath_update_chainmask(sc, 0); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ERP_PREAMBLE) { | ||||
| 		ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", | ||||
| 			  bss_conf->use_short_preamble); | ||||
| @ -3065,18 +3039,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 		ath9k_bss_assoc_info(sc, vif, bss_conf); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The HW TSF has to be reset when the beacon interval changes. | ||||
| 	 * We set the flag here, and ath_beacon_config_ap() would take this | ||||
| 	 * into account when it gets called through the subsequent | ||||
| 	 * config_interface() call - with IFCC_BEACON in the changed field. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT) { | ||||
| 		sc->sc_flags |= SC_OP_TSF_RESET; | ||||
| 		sc->beacon_interval = bss_conf->beacon_int; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&sc->mutex); | ||||
| } | ||||
| 
 | ||||
| @ -3118,6 +3080,7 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw) | ||||
| } | ||||
| 
 | ||||
| static int ath9k_ampdu_action(struct ieee80211_hw *hw, | ||||
| 			      struct ieee80211_vif *vif, | ||||
| 			      enum ieee80211_ampdu_mlme_action action, | ||||
| 			      struct ieee80211_sta *sta, | ||||
| 			      u16 tid, u16 *ssn) | ||||
| @ -3135,11 +3098,11 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, | ||||
| 		break; | ||||
| 	case IEEE80211_AMPDU_TX_START: | ||||
| 		ath_tx_aggr_start(sc, sta, tid, ssn); | ||||
| 		ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); | ||||
| 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); | ||||
| 		break; | ||||
| 	case IEEE80211_AMPDU_TX_STOP: | ||||
| 		ath_tx_aggr_stop(sc, sta, tid); | ||||
| 		ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); | ||||
| 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); | ||||
| 		break; | ||||
| 	case IEEE80211_AMPDU_TX_OPERATIONAL: | ||||
| 		ath_tx_aggr_resume(sc, sta, tid); | ||||
|  | ||||
| @ -527,95 +527,6 @@ static void ath9k_hw_force_bias(struct ath_hw *ah, u16 synth_freq) | ||||
| 	REG_WRITE_RF_ARRAY(&ah->iniBank6, ah->analogBank6Data, reg_writes); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ath9k_hw_decrease_chain_power() | ||||
|  * | ||||
|  * @ah: atheros hardware structure | ||||
|  * @chan: | ||||
|  * | ||||
|  * Only used on the AR5416 and AR5418 with the external AR2133/AR5133 radios. | ||||
|  * | ||||
|  * Sets a chain internal RF path to the lowest output power. Any | ||||
|  * further writes to bank6 after this setting will override these | ||||
|  * changes. Thus this function must be the last function in the | ||||
|  * sequence to modify bank 6. | ||||
|  * | ||||
|  * This function must be called after ar5416SetRfRegs() which is | ||||
|  * called from ath9k_hw_process_ini() due to swizzling of bank 6. | ||||
|  * Depends on ah->analogBank6Data being initialized by | ||||
|  * ath9k_hw_set_rf_regs() | ||||
|  * | ||||
|  * Additional additive reduction in power - | ||||
|  * change chain's switch table so chain's tx state is actually the rx | ||||
|  * state value. May produce different results in 2GHz/5GHz as well as | ||||
|  * board to board but in general should be a reduction. | ||||
|  * | ||||
|  * Activated by #ifdef ALTER_SWITCH.  Not tried yet.  If so, must be | ||||
|  * called after ah->eep_ops->set_board_values() due to RMW of | ||||
|  * PHY_SWITCH_CHAIN_0. | ||||
|  */ | ||||
| void ath9k_hw_decrease_chain_power(struct ath_hw *ah, | ||||
| 				   struct ath9k_channel *chan) | ||||
| { | ||||
| 	int i, regWrites = 0; | ||||
| 	u32 bank6SelMask; | ||||
| 	u32 *bank6Temp = ah->bank6Temp; | ||||
| 
 | ||||
| 	BUG_ON(AR_SREV_9280_10_OR_LATER(ah)); | ||||
| 
 | ||||
| 	switch (ah->config.diversity_control) { | ||||
| 	case ATH9K_ANT_FIXED_A: | ||||
| 		bank6SelMask = | ||||
| 		    (ah->config.antenna_switch_swap & ANTSWAP_AB) ? | ||||
| 			REDUCE_CHAIN_0 : /* swapped, reduce chain 0 */ | ||||
| 			REDUCE_CHAIN_1; /* normal, select chain 1/2 to reduce */ | ||||
| 		break; | ||||
| 	case ATH9K_ANT_FIXED_B: | ||||
| 		bank6SelMask = | ||||
| 		    (ah->config.antenna_switch_swap & ANTSWAP_AB) ? | ||||
| 			REDUCE_CHAIN_1 : /* swapped, reduce chain 1/2 */ | ||||
| 			REDUCE_CHAIN_0; /* normal, select chain 0 to reduce */ | ||||
| 		break; | ||||
| 	case ATH9K_ANT_VARIABLE: | ||||
| 		return; /* do not change anything */ | ||||
| 		break; | ||||
| 	default: | ||||
| 		return; /* do not change anything */ | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < ah->iniBank6.ia_rows; i++) | ||||
| 		bank6Temp[i] = ah->analogBank6Data[i]; | ||||
| 
 | ||||
| 	/* Write Bank 5 to switch Bank 6 write to selected chain only */ | ||||
| 	REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Modify Bank6 selected chain to use lowest amplification. | ||||
| 	 * Modifies the parameters to a value of 1. | ||||
| 	 * Depends on existing bank 6 values to be cached in | ||||
| 	 * ah->analogBank6Data | ||||
| 	 */ | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0); | ||||
| 	ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0); | ||||
| 
 | ||||
| 	REG_WRITE_RF_ARRAY(&ah->iniBank6, bank6Temp, regWrites); | ||||
| 
 | ||||
| 	REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053); | ||||
| #ifdef ALTER_SWITCH | ||||
| 	REG_WRITE(ah, PHY_SWITCH_CHAIN_0, | ||||
| 		  (REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38) | ||||
| 		  | ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38)); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ath9k_hw_set_channel - tune to a channel on the external AR2133/AR5133 radios | ||||
|  * @ah: atheros hardware stucture | ||||
| @ -687,7 +598,6 @@ int ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) | ||||
| 	} | ||||
| 
 | ||||
| 	ath9k_hw_force_bias(ah, freq); | ||||
| 	ath9k_hw_decrease_chain_power(ah, chan); | ||||
| 
 | ||||
| 	reg32 = | ||||
| 	    (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | | ||||
|  | ||||
| @ -35,9 +35,6 @@ bool ath9k_hw_set_rf_regs(struct ath_hw *ah, | ||||
| 			  struct ath9k_channel *chan, | ||||
| 			  u16 modesIndex); | ||||
| 
 | ||||
| void ath9k_hw_decrease_chain_power(struct ath_hw *ah, | ||||
| 				   struct ath9k_channel *chan); | ||||
| 
 | ||||
| #define AR_PHY_BASE     0x9800 | ||||
| #define AR_PHY(_n)      (AR_PHY_BASE + ((_n)<<2)) | ||||
| 
 | ||||
|  | ||||
| @ -859,12 +859,12 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, | ||||
| static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 			      const struct ath_rate_table *rate_table, | ||||
| 			      struct ath_rate_priv *ath_rc_priv, | ||||
| 			      struct ath_tx_info_priv *tx_info_priv, | ||||
| 				  struct ieee80211_tx_info *tx_info, | ||||
| 			      int tx_rate, int xretries, int retries, | ||||
| 			      u32 now_msec) | ||||
| { | ||||
| 	bool state_change = false; | ||||
| 	int count; | ||||
| 	int count, n_bad_frames; | ||||
| 	u8 last_per; | ||||
| 	static u32 nretry_to_per_lookup[10] = { | ||||
| 		100 * 0 / 1, | ||||
| @ -880,6 +880,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 	}; | ||||
| 
 | ||||
| 	last_per = ath_rc_priv->per[tx_rate]; | ||||
| 	n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len; | ||||
| 
 | ||||
| 	if (xretries) { | ||||
| 		if (xretries == 1) { | ||||
| @ -907,7 +908,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 		if (retries >= count) | ||||
| 			retries = count - 1; | ||||
| 
 | ||||
| 		if (tx_info_priv->n_bad_frames) { | ||||
| 		if (n_bad_frames) { | ||||
| 			/* new_PER = 7/8*old_PER + 1/8*(currentPER)
 | ||||
| 			 * Assuming that n_frames is not 0.  The current PER | ||||
| 			 * from the retries is 100 * retries / (retries+1), | ||||
| @ -920,14 +921,14 @@ static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 			 * the above PER.  The expression below is a | ||||
| 			 * simplified version of the sum of these two terms. | ||||
| 			 */ | ||||
| 			if (tx_info_priv->n_frames > 0) { | ||||
| 				int n_frames, n_bad_frames; | ||||
| 			if (tx_info->status.ampdu_len > 0) { | ||||
| 				int n_frames, n_bad_tries; | ||||
| 				u8 cur_per, new_per; | ||||
| 
 | ||||
| 				n_bad_frames = retries * tx_info_priv->n_frames + | ||||
| 					tx_info_priv->n_bad_frames; | ||||
| 				n_frames = tx_info_priv->n_frames * (retries + 1); | ||||
| 				cur_per = (100 * n_bad_frames / n_frames) >> 3; | ||||
| 				n_bad_tries = retries * tx_info->status.ampdu_len + | ||||
| 					n_bad_frames; | ||||
| 				n_frames = tx_info->status.ampdu_len * (retries + 1); | ||||
| 				cur_per = (100 * n_bad_tries / n_frames) >> 3; | ||||
| 				new_per = (u8)(last_per - (last_per >> 3) + cur_per); | ||||
| 				ath_rc_priv->per[tx_rate] = new_per; | ||||
| 			} | ||||
| @ -943,8 +944,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 		 * this was a probe.  Otherwise, ignore the probe. | ||||
| 		 */ | ||||
| 		if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) { | ||||
| 			if (retries > 0 || 2 * tx_info_priv->n_bad_frames > | ||||
| 				tx_info_priv->n_frames) { | ||||
| 			if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) { | ||||
| 				/*
 | ||||
| 				 * Since we probed with just a single attempt, | ||||
| 				 * any retries means the probe failed.  Also, | ||||
| @ -1003,7 +1003,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 
 | ||||
| static void ath_rc_update_ht(struct ath_softc *sc, | ||||
| 			     struct ath_rate_priv *ath_rc_priv, | ||||
| 			     struct ath_tx_info_priv *tx_info_priv, | ||||
| 			     struct ieee80211_tx_info *tx_info, | ||||
| 			     int tx_rate, int xretries, int retries) | ||||
| { | ||||
| 	u32 now_msec = jiffies_to_msecs(jiffies); | ||||
| @ -1020,7 +1020,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, | ||||
| 
 | ||||
| 	/* Update PER first */ | ||||
| 	state_change = ath_rc_update_per(sc, rate_table, ath_rc_priv, | ||||
| 					 tx_info_priv, tx_rate, xretries, | ||||
| 					 tx_info, tx_rate, xretries, | ||||
| 					 retries, now_msec); | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -1098,7 +1098,6 @@ static void ath_rc_tx_status(struct ath_softc *sc, | ||||
| 			     struct ieee80211_tx_info *tx_info, | ||||
| 			     int final_ts_idx, int xretries, int long_retry) | ||||
| { | ||||
| 	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 	const struct ath_rate_table *rate_table; | ||||
| 	struct ieee80211_tx_rate *rates = tx_info->status.rates; | ||||
| 	u8 flags; | ||||
| @ -1124,9 +1123,8 @@ static void ath_rc_tx_status(struct ath_softc *sc, | ||||
| 					return; | ||||
| 
 | ||||
| 				rix = ath_rc_get_rateindex(rate_table, &rates[i]); | ||||
| 				ath_rc_update_ht(sc, ath_rc_priv, | ||||
| 						tx_info_priv, rix, | ||||
| 						xretries ? 1 : 2, | ||||
| 				ath_rc_update_ht(sc, ath_rc_priv, tx_info, | ||||
| 						rix, xretries ? 1 : 2, | ||||
| 						rates[i].count); | ||||
| 			} | ||||
| 		} | ||||
| @ -1149,8 +1147,7 @@ static void ath_rc_tx_status(struct ath_softc *sc, | ||||
| 		return; | ||||
| 
 | ||||
| 	rix = ath_rc_get_rateindex(rate_table, &rates[i]); | ||||
| 	ath_rc_update_ht(sc, ath_rc_priv, tx_info_priv, rix, | ||||
| 			 xretries, long_retry); | ||||
| 	ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry); | ||||
| } | ||||
| 
 | ||||
| static const | ||||
| @ -1301,23 +1298,30 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, | ||||
| { | ||||
| 	struct ath_softc *sc = priv; | ||||
| 	struct ath_rate_priv *ath_rc_priv = priv_sta; | ||||
| 	struct ath_tx_info_priv *tx_info_priv = NULL; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_hdr *hdr; | ||||
| 	int final_ts_idx, tx_status = 0, is_underrun = 0; | ||||
| 	int final_ts_idx = 0, tx_status = 0, is_underrun = 0; | ||||
| 	int long_retry = 0; | ||||
| 	__le16 fc; | ||||
| 	int i; | ||||
| 
 | ||||
| 	hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 	fc = hdr->frame_control; | ||||
| 	tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 	final_ts_idx = tx_info_priv->tx.ts_rateindex; | ||||
| 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { | ||||
| 		struct ieee80211_tx_rate *rate = &tx_info->status.rates[i]; | ||||
| 		if (!rate->count) | ||||
| 			break; | ||||
| 
 | ||||
| 		final_ts_idx = i; | ||||
| 		long_retry = rate->count - 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!priv_sta || !ieee80211_is_data(fc) || | ||||
| 	    !tx_info_priv->update_rc) | ||||
| 		goto exit; | ||||
| 	    !(tx_info->pad[0] & ATH_TX_INFO_UPDATE_RC)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (tx_info_priv->tx.ts_status & ATH9K_TXERR_FILT) | ||||
| 		goto exit; | ||||
| 	if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If underrun error is seen assume it as an excessive retry only | ||||
| @ -1325,20 +1329,17 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, | ||||
| 	 * Adjust the long retry as if the frame was tried hw->max_rate_tries | ||||
| 	 * times. This affects how ratectrl updates PER for the failed rate. | ||||
| 	 */ | ||||
| 	if (tx_info_priv->tx.ts_flags & | ||||
| 	    (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) && | ||||
| 	    ((sc->sc_ah->tx_trig_level) >= ath_rc_priv->tx_triglevel_max)) { | ||||
| 	if ((tx_info->pad[0] & ATH_TX_INFO_UNDERRUN) && | ||||
| 	    (sc->sc_ah->tx_trig_level >= ath_rc_priv->tx_triglevel_max)) { | ||||
| 		tx_status = 1; | ||||
| 		is_underrun = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((tx_info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) || | ||||
| 	    (tx_info_priv->tx.ts_status & ATH9K_TXERR_FIFO)) | ||||
| 	if (tx_info->pad[0] & ATH_TX_INFO_XRETRY) | ||||
| 		tx_status = 1; | ||||
| 
 | ||||
| 	ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status, | ||||
| 			 (is_underrun) ? sc->hw->max_rate_tries : | ||||
| 			 tx_info_priv->tx.ts_longretry); | ||||
| 			 (is_underrun) ? sc->hw->max_rate_tries : long_retry); | ||||
| 
 | ||||
| 	/* Check if aggregation has to be enabled for this tid */ | ||||
| 	if (conf_is_ht(&sc->hw->conf) && | ||||
| @ -1352,13 +1353,11 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, | ||||
| 			an = (struct ath_node *)sta->drv_priv; | ||||
| 
 | ||||
| 			if(ath_tx_aggr_check(sc, an, tid)) | ||||
| 				ieee80211_start_tx_ba_session(sc->hw, hdr->addr1, tid); | ||||
| 				ieee80211_start_tx_ba_session(sta, tid); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ath_debug_stat_rc(sc, skb); | ||||
| exit: | ||||
| 	kfree(tx_info_priv); | ||||
| } | ||||
| 
 | ||||
| static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, | ||||
|  | ||||
| @ -167,24 +167,18 @@ struct ath_rate_priv { | ||||
| 	struct ath_rate_softc *asc; | ||||
| }; | ||||
| 
 | ||||
| #define ATH_TX_INFO_FRAME_TYPE_INTERNAL	(1 << 0) | ||||
| #define ATH_TX_INFO_FRAME_TYPE_PAUSE	(1 << 1) | ||||
| #define ATH_TX_INFO_UPDATE_RC		(1 << 2) | ||||
| #define ATH_TX_INFO_XRETRY		(1 << 3) | ||||
| #define ATH_TX_INFO_UNDERRUN		(1 << 4) | ||||
| 
 | ||||
| enum ath9k_internal_frame_type { | ||||
| 	ATH9K_NOT_INTERNAL, | ||||
| 	ATH9K_INT_PAUSE, | ||||
| 	ATH9K_INT_UNPAUSE | ||||
| }; | ||||
| 
 | ||||
| struct ath_tx_info_priv { | ||||
| 	struct ath_wiphy *aphy; | ||||
| 	struct ath_tx_status tx; | ||||
| 	int n_frames; | ||||
| 	int n_bad_frames; | ||||
| 	bool update_rc; | ||||
| 	enum ath9k_internal_frame_type frame_type; | ||||
| }; | ||||
| 
 | ||||
| #define ATH_TX_INFO_PRIV(tx_info) \ | ||||
| 	((struct ath_tx_info_priv *)((tx_info)->rate_driver_data[0])) | ||||
| 
 | ||||
| void ath_rate_attach(struct ath_softc *sc); | ||||
| u8 ath_rate_findrateix(struct ath_softc *sc, u8 dot11_rate); | ||||
| int ath_rate_control_register(void); | ||||
|  | ||||
| @ -338,13 +338,11 @@ void ath9k_wiphy_chan_work(struct work_struct *work) | ||||
| void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | ||||
| { | ||||
| 	struct ath_wiphy *aphy = hw->priv; | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 
 | ||||
| 	if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE && | ||||
| 	if ((tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_PAUSE) && | ||||
| 	    aphy->state == ATH_WIPHY_PAUSING) { | ||||
| 		if (!(info->flags & IEEE80211_TX_STAT_ACK)) { | ||||
| 		if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) { | ||||
| 			printk(KERN_DEBUG "ath9k: %s: no ACK for pause " | ||||
| 			       "frame\n", wiphy_name(hw->wiphy)); | ||||
| 			/*
 | ||||
| @ -363,9 +361,6 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(tx_info_priv); | ||||
| 	tx_info->rate_driver_data[0] = NULL; | ||||
| 
 | ||||
| 	dev_kfree_skb(skb); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -251,6 +251,7 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) | ||||
| 
 | ||||
| 	ATH_TXBUF_RESET(tbf); | ||||
| 
 | ||||
| 	tbf->aphy = bf->aphy; | ||||
| 	tbf->bf_mpdu = bf->bf_mpdu; | ||||
| 	tbf->bf_buf_addr = bf->bf_buf_addr; | ||||
| 	*(tbf->bf_desc) = *(bf->bf_desc); | ||||
| @ -270,7 +271,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | ||||
| 	struct ieee80211_hw *hw; | ||||
| 	struct ieee80211_hdr *hdr; | ||||
| 	struct ieee80211_tx_info *tx_info; | ||||
| 	struct ath_tx_info_priv *tx_info_priv; | ||||
| 	struct ath_atx_tid *tid = NULL; | ||||
| 	struct ath_buf *bf_next, *bf_last = bf->bf_lastbf; | ||||
| 	struct ath_desc *ds = bf_last->bf_desc; | ||||
| @ -284,8 +284,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | ||||
| 	hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 
 | ||||
| 	tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	tx_info_priv = (struct ath_tx_info_priv *) tx_info->rate_driver_data[0]; | ||||
| 	hw = tx_info_priv->aphy->hw; | ||||
| 	hw = bf->aphy->hw; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 
 | ||||
| @ -464,7 +463,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, | ||||
| 	struct sk_buff *skb; | ||||
| 	struct ieee80211_tx_info *tx_info; | ||||
| 	struct ieee80211_tx_rate *rates; | ||||
| 	struct ath_tx_info_priv *tx_info_priv; | ||||
| 	u32 max_4ms_framelen, frmlen; | ||||
| 	u16 aggr_limit, legacy = 0; | ||||
| 	int i; | ||||
| @ -472,7 +470,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, | ||||
| 	skb = bf->bf_mpdu; | ||||
| 	tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	rates = tx_info->control.rates; | ||||
| 	tx_info_priv = (struct ath_tx_info_priv *)tx_info->rate_driver_data[0]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Find the lowest frame length among the rate series that will have a | ||||
| @ -702,7 +699,6 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, | ||||
| 		/* anchor last desc of aggregate */ | ||||
| 		ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc); | ||||
| 
 | ||||
| 		txq->axq_aggr_depth++; | ||||
| 		ath_tx_txqaddbuf(sc, txq, &bf_q); | ||||
| 		TX_STAT_INC(txq->axq_qnum, a_aggr); | ||||
| 
 | ||||
| @ -878,8 +874,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) | ||||
| 		INIT_LIST_HEAD(&txq->axq_acq); | ||||
| 		spin_lock_init(&txq->axq_lock); | ||||
| 		txq->axq_depth = 0; | ||||
| 		txq->axq_aggr_depth = 0; | ||||
| 		txq->axq_linkbuf = NULL; | ||||
| 		txq->axq_tx_inprogress = false; | ||||
| 		sc->tx.txqsetup |= 1<<qnum; | ||||
| 	} | ||||
| @ -1014,7 +1008,6 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) | ||||
| 
 | ||||
| 		if (list_empty(&txq->axq_q)) { | ||||
| 			txq->axq_link = NULL; | ||||
| 			txq->axq_linkbuf = NULL; | ||||
| 			spin_unlock_bh(&txq->axq_lock); | ||||
| 			break; | ||||
| 		} | ||||
| @ -1199,7 +1192,6 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, | ||||
| 
 | ||||
| 	list_splice_tail_init(head, &txq->axq_q); | ||||
| 	txq->axq_depth++; | ||||
| 	txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list); | ||||
| 
 | ||||
| 	ath_print(common, ATH_DBG_QUEUE, | ||||
| 		  "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth); | ||||
| @ -1560,21 +1552,26 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, | ||||
| 	struct ath_softc *sc = aphy->sc; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 	struct ath_tx_info_priv *tx_info_priv; | ||||
| 	int hdrlen; | ||||
| 	__le16 fc; | ||||
| 
 | ||||
| 	tx_info_priv = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC); | ||||
| 	if (unlikely(!tx_info_priv)) | ||||
| 		return -ENOMEM; | ||||
| 	tx_info->rate_driver_data[0] = tx_info_priv; | ||||
| 	tx_info_priv->aphy = aphy; | ||||
| 	tx_info_priv->frame_type = txctl->frame_type; | ||||
| 	tx_info->pad[0] = 0; | ||||
| 	switch (txctl->frame_type) { | ||||
| 	case ATH9K_NOT_INTERNAL: | ||||
| 		break; | ||||
| 	case ATH9K_INT_PAUSE: | ||||
| 		tx_info->pad[0] |= ATH_TX_INFO_FRAME_TYPE_PAUSE; | ||||
| 		/* fall through */ | ||||
| 	case ATH9K_INT_UNPAUSE: | ||||
| 		tx_info->pad[0] |= ATH_TX_INFO_FRAME_TYPE_INTERNAL; | ||||
| 		break; | ||||
| 	} | ||||
| 	hdrlen = ieee80211_get_hdrlen_from_skb(skb); | ||||
| 	fc = hdr->frame_control; | ||||
| 
 | ||||
| 	ATH_TXBUF_RESET(bf); | ||||
| 
 | ||||
| 	bf->aphy = aphy; | ||||
| 	bf->bf_frmlen = skb->len + FCS_LEN - (hdrlen & 3); | ||||
| 
 | ||||
| 	if (conf_is_ht(&hw->conf) && !is_pae(skb)) | ||||
| @ -1599,8 +1596,6 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, | ||||
| 					   skb->len, DMA_TO_DEVICE); | ||||
| 	if (unlikely(dma_mapping_error(sc->dev, bf->bf_dmacontext))) { | ||||
| 		bf->bf_mpdu = NULL; | ||||
| 		kfree(tx_info_priv); | ||||
| 		tx_info->rate_driver_data[0] = NULL; | ||||
| 		ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, | ||||
| 			  "dma_mapping_error() on TX\n"); | ||||
| 		return -ENOMEM; | ||||
| @ -1781,27 +1776,17 @@ exit: | ||||
| /*****************/ | ||||
| 
 | ||||
| static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, | ||||
| 			    int tx_flags) | ||||
| 			    struct ath_wiphy *aphy, int tx_flags) | ||||
| { | ||||
| 	struct ieee80211_hw *hw = sc->hw; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||||
| 	int hdrlen, padsize; | ||||
| 	int frame_type = ATH9K_NOT_INTERNAL; | ||||
| 
 | ||||
| 	ath_print(common, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb); | ||||
| 
 | ||||
| 	if (tx_info_priv) { | ||||
| 		hw = tx_info_priv->aphy->hw; | ||||
| 		frame_type = tx_info_priv->frame_type; | ||||
| 	} | ||||
| 
 | ||||
| 	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || | ||||
| 	    tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { | ||||
| 		kfree(tx_info_priv); | ||||
| 		tx_info->rate_driver_data[0] = NULL; | ||||
| 	} | ||||
| 	if (aphy) | ||||
| 		hw = aphy->hw; | ||||
| 
 | ||||
| 	if (tx_flags & ATH_TX_BAR) | ||||
| 		tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; | ||||
| @ -1833,10 +1818,10 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, | ||||
| 					SC_OP_WAIT_FOR_TX_ACK)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (frame_type == ATH9K_NOT_INTERNAL) | ||||
| 		ieee80211_tx_status(hw, skb); | ||||
| 	else | ||||
| 	if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL)) | ||||
| 		ath9k_tx_status(hw, skb); | ||||
| 	else | ||||
| 		ieee80211_tx_status(hw, skb); | ||||
| } | ||||
| 
 | ||||
| static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, | ||||
| @ -1859,7 +1844,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, | ||||
| 	} | ||||
| 
 | ||||
| 	dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); | ||||
| 	ath_tx_complete(sc, skb, tx_flags); | ||||
| 	ath_tx_complete(sc, skb, bf->aphy, tx_flags); | ||||
| 	ath_debug_stat_tx(sc, txq, bf); | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -1907,8 +1892,7 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, | ||||
| 	struct sk_buff *skb = bf->bf_mpdu; | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 	struct ieee80211_hw *hw = tx_info_priv->aphy->hw; | ||||
| 	struct ieee80211_hw *hw = bf->aphy->hw; | ||||
| 	u8 i, tx_rateindex; | ||||
| 
 | ||||
| 	if (txok) | ||||
| @ -1917,17 +1901,22 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, | ||||
| 	tx_rateindex = ds->ds_txstat.ts_rateindex; | ||||
| 	WARN_ON(tx_rateindex >= hw->max_rates); | ||||
| 
 | ||||
| 	tx_info_priv->update_rc = update_rc; | ||||
| 	if (update_rc) | ||||
| 		tx_info->pad[0] |= ATH_TX_INFO_UPDATE_RC; | ||||
| 	if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) | ||||
| 		tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; | ||||
| 
 | ||||
| 	if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 && | ||||
| 	    (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { | ||||
| 		if (ieee80211_is_data(hdr->frame_control)) { | ||||
| 			memcpy(&tx_info_priv->tx, &ds->ds_txstat, | ||||
| 			       sizeof(tx_info_priv->tx)); | ||||
| 			tx_info_priv->n_frames = bf->bf_nframes; | ||||
| 			tx_info_priv->n_bad_frames = nbad; | ||||
| 			if (ds->ds_txstat.ts_flags & | ||||
| 			    (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN)) | ||||
| 				tx_info->pad[0] |= ATH_TX_INFO_UNDERRUN; | ||||
| 			if ((ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) || | ||||
| 			    (ds->ds_txstat.ts_status & ATH9K_TXERR_FIFO)) | ||||
| 				tx_info->pad[0] |= ATH_TX_INFO_XRETRY; | ||||
| 			tx_info->status.ampdu_len = bf->bf_nframes; | ||||
| 			tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -1971,7 +1960,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) | ||||
| 		spin_lock_bh(&txq->axq_lock); | ||||
| 		if (list_empty(&txq->axq_q)) { | ||||
| 			txq->axq_link = NULL; | ||||
| 			txq->axq_linkbuf = NULL; | ||||
| 			spin_unlock_bh(&txq->axq_lock); | ||||
| 			break; | ||||
| 		} | ||||
| @ -2005,10 +1993,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) | ||||
| 			spin_unlock_bh(&txq->axq_lock); | ||||
| 			break; | ||||
| 		} | ||||
| 		if (bf->bf_desc == txq->axq_lastdsWithCTS) | ||||
| 			txq->axq_lastdsWithCTS = NULL; | ||||
| 		if (ds == txq->axq_gatingds) | ||||
| 			txq->axq_gatingds = NULL; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Remove ath_buf's of the same transmit unit from txq, | ||||
| @ -2022,9 +2006,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) | ||||
| 				&txq->axq_q, lastbf->list.prev); | ||||
| 
 | ||||
| 		txq->axq_depth--; | ||||
| 		if (bf_isaggr(bf)) | ||||
| 			txq->axq_aggr_depth--; | ||||
| 
 | ||||
| 		txok = (ds->ds_txstat.ts_status == 0); | ||||
| 		txq->axq_tx_inprogress = false; | ||||
| 		spin_unlock_bh(&txq->axq_lock); | ||||
|  | ||||
| @ -450,7 +450,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, | ||||
| 	const struct ieee80211_regdomain *regd; | ||||
| 
 | ||||
| 	wiphy->reg_notifier = reg_notifier; | ||||
| 	wiphy->strict_regulatory = true; | ||||
| 	wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; | ||||
| 
 | ||||
| 	if (ath_is_world_regd(reg)) { | ||||
| 		/*
 | ||||
| @ -458,8 +458,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, | ||||
| 		 * saved on the wiphy orig_* parameters | ||||
| 		 */ | ||||
| 		regd = ath_world_regdomain(reg); | ||||
| 		wiphy->custom_regulatory = true; | ||||
| 		wiphy->strict_regulatory = false; | ||||
| 		wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * This gets applied in the case of the absense of CRDA, | ||||
|  | ||||
| @ -383,44 +383,160 @@ static inline | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Check if a DMA region fits the device constraints.
 | ||||
|  * Returns true, if the region is OK for usage with this device. */ | ||||
| static inline bool b43_dma_address_ok(struct b43_dmaring *ring, | ||||
| 				      dma_addr_t addr, size_t size) | ||||
| { | ||||
| 	switch (ring->type) { | ||||
| 	case B43_DMA_30BIT: | ||||
| 		if ((u64)addr + size > (1ULL << 30)) | ||||
| 			return 0; | ||||
| 		break; | ||||
| 	case B43_DMA_32BIT: | ||||
| 		if ((u64)addr + size > (1ULL << 32)) | ||||
| 			return 0; | ||||
| 		break; | ||||
| 	case B43_DMA_64BIT: | ||||
| 		/* Currently we can't have addresses beyond
 | ||||
| 		 * 64bit in the kernel. */ | ||||
| 		break; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| #define is_4k_aligned(addr)	(((u64)(addr) & 0x0FFFull) == 0) | ||||
| #define is_8k_aligned(addr)	(((u64)(addr) & 0x1FFFull) == 0) | ||||
| 
 | ||||
| static void b43_unmap_and_free_ringmem(struct b43_dmaring *ring, void *base, | ||||
| 				       dma_addr_t dmaaddr, size_t size) | ||||
| { | ||||
| 	ssb_dma_unmap_single(ring->dev->dev, dmaaddr, size, DMA_TO_DEVICE); | ||||
| 	free_pages((unsigned long)base, get_order(size)); | ||||
| } | ||||
| 
 | ||||
| static void * __b43_get_and_map_ringmem(struct b43_dmaring *ring, | ||||
| 					dma_addr_t *dmaaddr, size_t size, | ||||
| 					gfp_t gfp_flags) | ||||
| { | ||||
| 	void *base; | ||||
| 
 | ||||
| 	base = (void *)__get_free_pages(gfp_flags, get_order(size)); | ||||
| 	if (!base) | ||||
| 		return NULL; | ||||
| 	memset(base, 0, size); | ||||
| 	*dmaaddr = ssb_dma_map_single(ring->dev->dev, base, size, | ||||
| 				      DMA_TO_DEVICE); | ||||
| 	if (ssb_dma_mapping_error(ring->dev->dev, *dmaaddr)) { | ||||
| 		free_pages((unsigned long)base, get_order(size)); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return base; | ||||
| } | ||||
| 
 | ||||
| static void * b43_get_and_map_ringmem(struct b43_dmaring *ring, | ||||
| 				      dma_addr_t *dmaaddr, size_t size) | ||||
| { | ||||
| 	void *base; | ||||
| 
 | ||||
| 	base = __b43_get_and_map_ringmem(ring, dmaaddr, size, | ||||
| 					 GFP_KERNEL); | ||||
| 	if (!base) { | ||||
| 		b43err(ring->dev->wl, "Failed to allocate or map pages " | ||||
| 		       "for DMA ringmemory\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (!b43_dma_address_ok(ring, *dmaaddr, size)) { | ||||
| 		/* The memory does not fit our device constraints.
 | ||||
| 		 * Retry with GFP_DMA set to get lower memory. */ | ||||
| 		b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size); | ||||
| 		base = __b43_get_and_map_ringmem(ring, dmaaddr, size, | ||||
| 						 GFP_KERNEL | GFP_DMA); | ||||
| 		if (!base) { | ||||
| 			b43err(ring->dev->wl, "Failed to allocate or map pages " | ||||
| 			       "in the GFP_DMA region for DMA ringmemory\n"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 		if (!b43_dma_address_ok(ring, *dmaaddr, size)) { | ||||
| 			b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size); | ||||
| 			b43err(ring->dev->wl, "Failed to allocate DMA " | ||||
| 			       "ringmemory that fits device constraints\n"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 	/* We expect the memory to be 4k aligned, at least. */ | ||||
| 	if (B43_WARN_ON(!is_4k_aligned(*dmaaddr))) { | ||||
| 		b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return base; | ||||
| } | ||||
| 
 | ||||
| static int alloc_ringmemory(struct b43_dmaring *ring) | ||||
| { | ||||
| 	gfp_t flags = GFP_KERNEL; | ||||
| 	unsigned int required; | ||||
| 	void *base; | ||||
| 	dma_addr_t dmaaddr; | ||||
| 
 | ||||
| 	/* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
 | ||||
| 	 * alignment and 8K buffers for 64-bit DMA with 8K alignment. Testing | ||||
| 	 * has shown that 4K is sufficient for the latter as long as the buffer | ||||
| 	 * does not cross an 8K boundary. | ||||
| 	 * | ||||
| 	 * For unknown reasons - possibly a hardware error - the BCM4311 rev | ||||
| 	 * 02, which uses 64-bit DMA, needs the ring buffer in very low memory, | ||||
| 	 * which accounts for the GFP_DMA flag below. | ||||
| 	 * | ||||
| 	 * The flags here must match the flags in free_ringmemory below! | ||||
| 	/* There are several requirements to the descriptor ring memory:
 | ||||
| 	 * - The memory region needs to fit the address constraints for the | ||||
| 	 *   device (same as for frame buffers). | ||||
| 	 * - For 30/32bit DMA devices, the descriptor ring must be 4k aligned. | ||||
| 	 * - For 64bit DMA devices, the descriptor ring must be 8k aligned. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (ring->type == B43_DMA_64BIT) | ||||
| 		flags |= GFP_DMA; | ||||
| 	ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev, | ||||
| 						  B43_DMA_RINGMEMSIZE, | ||||
| 						  &(ring->dmabase), flags); | ||||
| 	if (!ring->descbase) { | ||||
| 		b43err(ring->dev->wl, "DMA ringmemory allocation failed\n"); | ||||
| 		required = ring->nr_slots * sizeof(struct b43_dmadesc64); | ||||
| 	else | ||||
| 		required = ring->nr_slots * sizeof(struct b43_dmadesc32); | ||||
| 	if (B43_WARN_ON(required > 0x1000)) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ring->alloc_descsize = 0x1000; | ||||
| 	base = b43_get_and_map_ringmem(ring, &dmaaddr, ring->alloc_descsize); | ||||
| 	if (!base) | ||||
| 		return -ENOMEM; | ||||
| 	ring->alloc_descbase = base; | ||||
| 	ring->alloc_dmabase = dmaaddr; | ||||
| 
 | ||||
| 	if ((ring->type != B43_DMA_64BIT) || is_8k_aligned(dmaaddr)) { | ||||
| 		/* We're on <=32bit DMA, or we already got 8k aligned memory.
 | ||||
| 		 * That's all we need, so we're fine. */ | ||||
| 		ring->descbase = base; | ||||
| 		ring->dmabase = dmaaddr; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	memset(ring->descbase, 0, B43_DMA_RINGMEMSIZE); | ||||
| 	b43_unmap_and_free_ringmem(ring, base, dmaaddr, ring->alloc_descsize); | ||||
| 
 | ||||
| 	/* Ok, we failed at the 8k alignment requirement.
 | ||||
| 	 * Try to force-align the memory region now. */ | ||||
| 	ring->alloc_descsize = 0x2000; | ||||
| 	base = b43_get_and_map_ringmem(ring, &dmaaddr, ring->alloc_descsize); | ||||
| 	if (!base) | ||||
| 		return -ENOMEM; | ||||
| 	ring->alloc_descbase = base; | ||||
| 	ring->alloc_dmabase = dmaaddr; | ||||
| 
 | ||||
| 	if (is_8k_aligned(dmaaddr)) { | ||||
| 		/* We're already 8k aligned. That Ok, too. */ | ||||
| 		ring->descbase = base; | ||||
| 		ring->dmabase = dmaaddr; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	/* Force-align it to 8k */ | ||||
| 	ring->descbase = (void *)((u8 *)base + 0x1000); | ||||
| 	ring->dmabase = dmaaddr + 0x1000; | ||||
| 	B43_WARN_ON(!is_8k_aligned(ring->dmabase)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void free_ringmemory(struct b43_dmaring *ring) | ||||
| { | ||||
| 	gfp_t flags = GFP_KERNEL; | ||||
| 
 | ||||
| 	if (ring->type == B43_DMA_64BIT) | ||||
| 		flags |= GFP_DMA; | ||||
| 
 | ||||
| 	ssb_dma_free_consistent(ring->dev->dev, B43_DMA_RINGMEMSIZE, | ||||
| 				ring->descbase, ring->dmabase, flags); | ||||
| 	b43_unmap_and_free_ringmem(ring, ring->alloc_descbase, | ||||
| 				   ring->alloc_dmabase, ring->alloc_descsize); | ||||
| } | ||||
| 
 | ||||
| /* Reset the RX DMA channel */ | ||||
| @ -530,29 +646,14 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring, | ||||
| 	if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr))) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	switch (ring->type) { | ||||
| 	case B43_DMA_30BIT: | ||||
| 		if ((u64)addr + buffersize > (1ULL << 30)) | ||||
| 			goto address_error; | ||||
| 		break; | ||||
| 	case B43_DMA_32BIT: | ||||
| 		if ((u64)addr + buffersize > (1ULL << 32)) | ||||
| 			goto address_error; | ||||
| 		break; | ||||
| 	case B43_DMA_64BIT: | ||||
| 		/* Currently we can't have addresses beyond
 | ||||
| 		 * 64bit in the kernel. */ | ||||
| 		break; | ||||
| 	if (!b43_dma_address_ok(ring, addr, buffersize)) { | ||||
| 		/* We can't support this address. Unmap it again. */ | ||||
| 		unmap_descbuffer(ring, addr, buffersize, dma_to_device); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* The address is OK. */ | ||||
| 	return 0; | ||||
| 
 | ||||
| address_error: | ||||
| 	/* We can't support this address. Unmap it again. */ | ||||
| 	unmap_descbuffer(ring, addr, buffersize, dma_to_device); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) | ||||
| @ -614,6 +715,9 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring, | ||||
| 	meta->dmaaddr = dmaaddr; | ||||
| 	ring->ops->fill_descriptor(ring, desc, dmaaddr, | ||||
| 				   ring->rx_buffersize, 0, 0, 0); | ||||
| 	ssb_dma_sync_single_for_device(ring->dev->dev, | ||||
| 				       ring->alloc_dmabase, | ||||
| 				       ring->alloc_descsize, DMA_TO_DEVICE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -770,7 +874,7 @@ static void free_all_descbuffers(struct b43_dmaring *ring) | ||||
| 	for (i = 0; i < ring->nr_slots; i++) { | ||||
| 		desc = ring->ops->idx2desc(ring, i, &meta); | ||||
| 
 | ||||
| 		if (!meta->skb) { | ||||
| 		if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { | ||||
| 			B43_WARN_ON(!ring->tx); | ||||
| 			continue; | ||||
| 		} | ||||
| @ -822,7 +926,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, | ||||
| 				      enum b43_dmatype type) | ||||
| { | ||||
| 	struct b43_dmaring *ring; | ||||
| 	int err; | ||||
| 	int i, err; | ||||
| 	dma_addr_t dma_test; | ||||
| 
 | ||||
| 	ring = kzalloc(sizeof(*ring), GFP_KERNEL); | ||||
| @ -837,6 +941,8 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, | ||||
| 			     GFP_KERNEL); | ||||
| 	if (!ring->meta) | ||||
| 		goto err_kfree_ring; | ||||
| 	for (i = 0; i < ring->nr_slots; i++) | ||||
| 		ring->meta->skb = B43_DMA_PTR_POISON; | ||||
| 
 | ||||
| 	ring->type = type; | ||||
| 	ring->dev = dev; | ||||
| @ -1147,11 +1253,13 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) | ||||
| 	case 0x5000: | ||||
| 		ring = dma->tx_ring_mcast; | ||||
| 		break; | ||||
| 	default: | ||||
| 		B43_WARN_ON(1); | ||||
| 	} | ||||
| 	*slot = (cookie & 0x0FFF); | ||||
| 	B43_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); | ||||
| 	if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { | ||||
| 		b43dbg(dev->wl, "TX-status contains " | ||||
| 		       "invalid cookie: 0x%04X\n", cookie); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return ring; | ||||
| } | ||||
| @ -1246,6 +1354,9 @@ static int dma_tx_fragment(struct b43_dmaring *ring, | ||||
| 	} | ||||
| 	/* Now transfer the whole frame. */ | ||||
| 	wmb(); | ||||
| 	ssb_dma_sync_single_for_device(ring->dev->dev, | ||||
| 				       ring->alloc_dmabase, | ||||
| 				       ring->alloc_descsize, DMA_TO_DEVICE); | ||||
| 	ops->poke_tx(ring, next_slot(ring, slot)); | ||||
| 	return 0; | ||||
| 
 | ||||
| @ -1387,19 +1498,40 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, | ||||
| 	struct b43_dmaring *ring; | ||||
| 	struct b43_dmadesc_generic *desc; | ||||
| 	struct b43_dmadesc_meta *meta; | ||||
| 	int slot; | ||||
| 	int slot, firstused; | ||||
| 	bool frame_succeed; | ||||
| 
 | ||||
| 	ring = parse_cookie(dev, status->cookie, &slot); | ||||
| 	if (unlikely(!ring)) | ||||
| 		return; | ||||
| 
 | ||||
| 	B43_WARN_ON(!ring->tx); | ||||
| 
 | ||||
| 	/* Sanity check: TX packets are processed in-order on one ring.
 | ||||
| 	 * Check if the slot deduced from the cookie really is the first | ||||
| 	 * used slot. */ | ||||
| 	firstused = ring->current_slot - ring->used_slots + 1; | ||||
| 	if (firstused < 0) | ||||
| 		firstused = ring->nr_slots + firstused; | ||||
| 	if (unlikely(slot != firstused)) { | ||||
| 		/* This possibly is a firmware bug and will result in
 | ||||
| 		 * malfunction, memory leaks and/or stall of DMA functionality. */ | ||||
| 		b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. " | ||||
| 		       "Expected %d, but got %d\n", | ||||
| 		       ring->index, firstused, slot); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ops = ring->ops; | ||||
| 	while (1) { | ||||
| 		B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); | ||||
| 		B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); | ||||
| 		desc = ops->idx2desc(ring, slot, &meta); | ||||
| 
 | ||||
| 		if (b43_dma_ptr_is_poisoned(meta->skb)) { | ||||
| 			b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " | ||||
| 			       "on ring %d\n", | ||||
| 			       slot, firstused, ring->index); | ||||
| 			break; | ||||
| 		} | ||||
| 		if (meta->skb) { | ||||
| 			struct b43_private_tx_info *priv_info = | ||||
| 				b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); | ||||
| @ -1415,7 +1547,14 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, | ||||
| 		if (meta->is_last_fragment) { | ||||
| 			struct ieee80211_tx_info *info; | ||||
| 
 | ||||
| 			BUG_ON(!meta->skb); | ||||
| 			if (unlikely(!meta->skb)) { | ||||
| 				/* This is a scatter-gather fragment of a frame, so
 | ||||
| 				 * the skb pointer must not be NULL. */ | ||||
| 				b43dbg(dev->wl, "TX status unexpected NULL skb " | ||||
| 				       "at slot %d (first=%d) on ring %d\n", | ||||
| 				       slot, firstused, ring->index); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			info = IEEE80211_SKB_CB(meta->skb); | ||||
| 
 | ||||
| @ -1433,20 +1572,29 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, | ||||
| #endif /* DEBUG */ | ||||
| 			ieee80211_tx_status(dev->wl->hw, meta->skb); | ||||
| 
 | ||||
| 			/* skb is freed by ieee80211_tx_status() */ | ||||
| 			meta->skb = NULL; | ||||
| 			/* skb will be freed by ieee80211_tx_status().
 | ||||
| 			 * Poison our pointer. */ | ||||
| 			meta->skb = B43_DMA_PTR_POISON; | ||||
| 		} else { | ||||
| 			/* No need to call free_descriptor_buffer here, as
 | ||||
| 			 * this is only the txhdr, which is not allocated. | ||||
| 			 */ | ||||
| 			B43_WARN_ON(meta->skb); | ||||
| 			if (unlikely(meta->skb)) { | ||||
| 				b43dbg(dev->wl, "TX status unexpected non-NULL skb " | ||||
| 				       "at slot %d (first=%d) on ring %d\n", | ||||
| 				       slot, firstused, ring->index); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* Everything unmapped and free'd. So it's not used anymore. */ | ||||
| 		ring->used_slots--; | ||||
| 
 | ||||
| 		if (meta->is_last_fragment) | ||||
| 		if (meta->is_last_fragment) { | ||||
| 			/* This is the last scatter-gather
 | ||||
| 			 * fragment of the frame. We are done. */ | ||||
| 			break; | ||||
| 		} | ||||
| 		slot = next_slot(ring, slot); | ||||
| 	} | ||||
| 	if (ring->stopped) { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #ifndef B43_DMA_H_ | ||||
| #define B43_DMA_H_ | ||||
| 
 | ||||
| #include <linux/ieee80211.h> | ||||
| #include <linux/err.h> | ||||
| 
 | ||||
| #include "b43.h" | ||||
| 
 | ||||
| @ -157,7 +157,6 @@ struct b43_dmadesc_generic { | ||||
| } __attribute__ ((__packed__)); | ||||
| 
 | ||||
| /* Misc DMA constants */ | ||||
| #define B43_DMA_RINGMEMSIZE		PAGE_SIZE | ||||
| #define B43_DMA0_RX_FRAMEOFFSET		30 | ||||
| 
 | ||||
| /* DMA engine tuning knobs */ | ||||
| @ -165,6 +164,10 @@ struct b43_dmadesc_generic { | ||||
| #define B43_RXRING_SLOTS		64 | ||||
| #define B43_DMA0_RX_BUFFERSIZE		IEEE80211_MAX_FRAME_LEN | ||||
| 
 | ||||
| /* Pointer poison */ | ||||
| #define B43_DMA_PTR_POISON		((void *)ERR_PTR(-ENOMEM)) | ||||
| #define b43_dma_ptr_is_poisoned(ptr)	(unlikely((ptr) == B43_DMA_PTR_POISON)) | ||||
| 
 | ||||
| 
 | ||||
| struct sk_buff; | ||||
| struct b43_private; | ||||
| @ -243,6 +246,12 @@ struct b43_dmaring { | ||||
| 	/* The QOS priority assigned to this ring. Only used for TX rings.
 | ||||
| 	 * This is the mac80211 "queue" value. */ | ||||
| 	u8 queue_prio; | ||||
| 	/* Pointers and size of the originally allocated and mapped memory
 | ||||
| 	 * region for the descriptor ring. */ | ||||
| 	void *alloc_descbase; | ||||
| 	dma_addr_t alloc_dmabase; | ||||
| 	unsigned int alloc_descsize; | ||||
| 	/* Pointer to our wireless device. */ | ||||
| 	struct b43_wldev *dev; | ||||
| #ifdef CONFIG_B43_DEBUG | ||||
| 	/* Maximum number of used slots. */ | ||||
|  | ||||
| @ -296,6 +296,33 @@ static const char *command_types[] = { | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| #define WEXT_USECHANNELS 1 | ||||
| 
 | ||||
| static const long ipw2100_frequencies[] = { | ||||
| 	2412, 2417, 2422, 2427, | ||||
| 	2432, 2437, 2442, 2447, | ||||
| 	2452, 2457, 2462, 2467, | ||||
| 	2472, 2484 | ||||
| }; | ||||
| 
 | ||||
| #define FREQ_COUNT	ARRAY_SIZE(ipw2100_frequencies) | ||||
| 
 | ||||
| static const long ipw2100_rates_11b[] = { | ||||
| 	1000000, | ||||
| 	2000000, | ||||
| 	5500000, | ||||
| 	11000000 | ||||
| }; | ||||
| 
 | ||||
| static struct ieee80211_rate ipw2100_bg_rates[] = { | ||||
| 	{ .bitrate = 10 }, | ||||
| 	{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||||
| 	{ .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||||
| 	{ .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||||
| }; | ||||
| 
 | ||||
| #define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b) | ||||
| 
 | ||||
| /* Pre-decl until we get the code solid and then we can clean it up */ | ||||
| static void ipw2100_tx_send_commands(struct ipw2100_priv *priv); | ||||
| static void ipw2100_tx_send_data(struct ipw2100_priv *priv); | ||||
| @ -1141,6 +1168,7 @@ static int rf_kill_active(struct ipw2100_priv *priv) | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!(priv->hw_features & HW_FEATURE_RFKILL)) { | ||||
| 		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); | ||||
| 		priv->status &= ~STATUS_RF_KILL_HW; | ||||
| 		return 0; | ||||
| 	} | ||||
| @ -1151,10 +1179,13 @@ static int rf_kill_active(struct ipw2100_priv *priv) | ||||
| 		value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (value == 0) | ||||
| 	if (value == 0) { | ||||
| 		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); | ||||
| 		priv->status |= STATUS_RF_KILL_HW; | ||||
| 	else | ||||
| 	} else { | ||||
| 		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); | ||||
| 		priv->status &= ~STATUS_RF_KILL_HW; | ||||
| 	} | ||||
| 
 | ||||
| 	return (value == 0); | ||||
| } | ||||
| @ -1814,13 +1845,6 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /* Called by register_netdev() */ | ||||
| static int ipw2100_net_init(struct net_device *dev) | ||||
| { | ||||
| 	struct ipw2100_priv *priv = libipw_priv(dev); | ||||
| 	return ipw2100_up(priv, 1); | ||||
| } | ||||
| 
 | ||||
| static void ipw2100_down(struct ipw2100_priv *priv) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| @ -1875,6 +1899,64 @@ static void ipw2100_down(struct ipw2100_priv *priv) | ||||
| 	netif_stop_queue(priv->net_dev); | ||||
| } | ||||
| 
 | ||||
| /* Called by register_netdev() */ | ||||
| static int ipw2100_net_init(struct net_device *dev) | ||||
| { | ||||
| 	struct ipw2100_priv *priv = libipw_priv(dev); | ||||
| 	const struct libipw_geo *geo = libipw_get_geo(priv->ieee); | ||||
| 	struct wireless_dev *wdev = &priv->ieee->wdev; | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 
 | ||||
| 	ret = ipw2100_up(priv, 1); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); | ||||
| 
 | ||||
| 	/* fill-out priv->ieee->bg_band */ | ||||
| 	if (geo->bg_channels) { | ||||
| 		struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; | ||||
| 
 | ||||
| 		bg_band->band = IEEE80211_BAND_2GHZ; | ||||
| 		bg_band->n_channels = geo->bg_channels; | ||||
| 		bg_band->channels = | ||||
| 			kzalloc(geo->bg_channels * | ||||
| 				sizeof(struct ieee80211_channel), GFP_KERNEL); | ||||
| 		/* translate geo->bg to bg_band.channels */ | ||||
| 		for (i = 0; i < geo->bg_channels; i++) { | ||||
| 			bg_band->channels[i].band = IEEE80211_BAND_2GHZ; | ||||
| 			bg_band->channels[i].center_freq = geo->bg[i].freq; | ||||
| 			bg_band->channels[i].hw_value = geo->bg[i].channel; | ||||
| 			bg_band->channels[i].max_power = geo->bg[i].max_power; | ||||
| 			if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) | ||||
| 				bg_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_PASSIVE_SCAN; | ||||
| 			if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) | ||||
| 				bg_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_NO_IBSS; | ||||
| 			if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) | ||||
| 				bg_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_RADAR; | ||||
| 			/* No equivalent for LIBIPW_CH_80211H_RULES,
 | ||||
| 			   LIBIPW_CH_UNIFORM_SPREADING, or | ||||
| 			   LIBIPW_CH_B_ONLY... */ | ||||
| 		} | ||||
| 		/* point at bitrate info */ | ||||
| 		bg_band->bitrates = ipw2100_bg_rates; | ||||
| 		bg_band->n_bitrates = RATE_COUNT; | ||||
| 
 | ||||
| 		wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; | ||||
| 	} | ||||
| 
 | ||||
| 	set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); | ||||
| 	if (wiphy_register(wdev->wiphy)) { | ||||
| 		ipw2100_down(priv); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void ipw2100_reset_adapter(struct work_struct *work) | ||||
| { | ||||
| 	struct ipw2100_priv *priv = | ||||
| @ -2090,6 +2172,7 @@ static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status) | ||||
| 		       priv->net_dev->name); | ||||
| 
 | ||||
| 	/* RF_KILL is now enabled (else we wouldn't be here) */ | ||||
| 	wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); | ||||
| 	priv->status |= STATUS_RF_KILL_HW; | ||||
| 
 | ||||
| 	/* Make sure the RF Kill check timer is running */ | ||||
| @ -6029,7 +6112,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, | ||||
| 	struct ipw2100_priv *priv; | ||||
| 	struct net_device *dev; | ||||
| 
 | ||||
| 	dev = alloc_ieee80211(sizeof(struct ipw2100_priv)); | ||||
| 	dev = alloc_ieee80211(sizeof(struct ipw2100_priv), 0); | ||||
| 	if (!dev) | ||||
| 		return NULL; | ||||
| 	priv = libipw_priv(dev); | ||||
| @ -6342,7 +6425,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, | ||||
| 		sysfs_remove_group(&pci_dev->dev.kobj, | ||||
| 				   &ipw2100_attribute_group); | ||||
| 
 | ||||
| 		free_ieee80211(dev); | ||||
| 		free_ieee80211(dev, 0); | ||||
| 		pci_set_drvdata(pci_dev, NULL); | ||||
| 	} | ||||
| 
 | ||||
| @ -6400,7 +6483,10 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev) | ||||
| 		if (dev->base_addr) | ||||
| 			iounmap((void __iomem *)dev->base_addr); | ||||
| 
 | ||||
| 		free_ieee80211(dev); | ||||
| 		/* wiphy_unregister needs to be here, before free_ieee80211 */ | ||||
| 		wiphy_unregister(priv->ieee->wdev.wiphy); | ||||
| 		kfree(priv->ieee->bg_band.channels); | ||||
| 		free_ieee80211(dev, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	pci_release_regions(pci_dev); | ||||
| @ -6601,26 +6687,6 @@ static void __exit ipw2100_exit(void) | ||||
| module_init(ipw2100_init); | ||||
| module_exit(ipw2100_exit); | ||||
| 
 | ||||
| #define WEXT_USECHANNELS 1 | ||||
| 
 | ||||
| static const long ipw2100_frequencies[] = { | ||||
| 	2412, 2417, 2422, 2427, | ||||
| 	2432, 2437, 2442, 2447, | ||||
| 	2452, 2457, 2462, 2467, | ||||
| 	2472, 2484 | ||||
| }; | ||||
| 
 | ||||
| #define FREQ_COUNT	ARRAY_SIZE(ipw2100_frequencies) | ||||
| 
 | ||||
| static const long ipw2100_rates_11b[] = { | ||||
| 	1000000, | ||||
| 	2000000, | ||||
| 	5500000, | ||||
| 	11000000 | ||||
| }; | ||||
| 
 | ||||
| #define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b) | ||||
| 
 | ||||
| static int ipw2100_wx_get_name(struct net_device *dev, | ||||
| 			       struct iw_request_info *info, | ||||
| 			       union iwreq_data *wrqu, char *extra) | ||||
|  | ||||
| @ -109,6 +109,25 @@ static int antenna = CFG_SYS_ANTENNA_BOTH; | ||||
| static int rtap_iface = 0;     /* def: 0 -- do not create rtap interface */ | ||||
| #endif | ||||
| 
 | ||||
| static struct ieee80211_rate ipw2200_rates[] = { | ||||
| 	{ .bitrate = 10 }, | ||||
| 	{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||||
| 	{ .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||||
| 	{ .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||||
| 	{ .bitrate = 60 }, | ||||
| 	{ .bitrate = 90 }, | ||||
| 	{ .bitrate = 120 }, | ||||
| 	{ .bitrate = 180 }, | ||||
| 	{ .bitrate = 240 }, | ||||
| 	{ .bitrate = 360 }, | ||||
| 	{ .bitrate = 480 }, | ||||
| 	{ .bitrate = 540 } | ||||
| }; | ||||
| 
 | ||||
| #define ipw2200_a_rates		(ipw2200_rates + 4) | ||||
| #define ipw2200_num_a_rates	8 | ||||
| #define ipw2200_bg_rates	(ipw2200_rates + 0) | ||||
| #define ipw2200_num_bg_rates	12 | ||||
| 
 | ||||
| #ifdef CONFIG_IPW2200_QOS | ||||
| static int qos_enable = 0; | ||||
| @ -1739,10 +1758,13 @@ static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO, | ||||
| 
 | ||||
| static int rf_kill_active(struct ipw_priv *priv) | ||||
| { | ||||
| 	if (0 == (ipw_read32(priv, 0x30) & 0x10000)) | ||||
| 	if (0 == (ipw_read32(priv, 0x30) & 0x10000)) { | ||||
| 		priv->status |= STATUS_RF_KILL_HW; | ||||
| 	else | ||||
| 		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); | ||||
| 	} else { | ||||
| 		priv->status &= ~STATUS_RF_KILL_HW; | ||||
| 		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); | ||||
| 	} | ||||
| 
 | ||||
| 	return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0; | ||||
| } | ||||
| @ -2025,6 +2047,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv) | ||||
| 	if (inta & IPW_INTA_BIT_RF_KILL_DONE) { | ||||
| 		IPW_DEBUG_RF_KILL("RF_KILL_DONE\n"); | ||||
| 		priv->status |= STATUS_RF_KILL_HW; | ||||
| 		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); | ||||
| 		wake_up_interruptible(&priv->wait_command_queue); | ||||
| 		priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); | ||||
| 		cancel_delayed_work(&priv->request_scan); | ||||
| @ -8660,24 +8683,6 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static int ipw_wx_get_name(struct net_device *dev, | ||||
| 			   struct iw_request_info *info, | ||||
| 			   union iwreq_data *wrqu, char *extra) | ||||
| { | ||||
| 	struct ipw_priv *priv = libipw_priv(dev); | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	if (priv->status & STATUS_RF_KILL_MASK) | ||||
| 		strcpy(wrqu->name, "radio off"); | ||||
| 	else if (!(priv->status & STATUS_ASSOCIATED)) | ||||
| 		strcpy(wrqu->name, "unassociated"); | ||||
| 	else | ||||
| 		snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c", | ||||
| 			 ipw_modes[priv->assoc_request.ieee_mode]); | ||||
| 	IPW_DEBUG_WX("Name: %s\n", wrqu->name); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ipw_set_channel(struct ipw_priv *priv, u8 channel) | ||||
| { | ||||
| 	if (channel == 0) { | ||||
| @ -9977,7 +9982,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, | ||||
| /* Rebase the WE IOCTLs to zero for the handler array */ | ||||
| #define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT] | ||||
| static iw_handler ipw_wx_handlers[] = { | ||||
| 	IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name, | ||||
| 	IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, | ||||
| 	IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, | ||||
| 	IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, | ||||
| 	IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, | ||||
| @ -11422,16 +11427,100 @@ static void ipw_bg_down(struct work_struct *work) | ||||
| /* Called by register_netdev() */ | ||||
| static int ipw_net_init(struct net_device *dev) | ||||
| { | ||||
| 	int i, rc = 0; | ||||
| 	struct ipw_priv *priv = libipw_priv(dev); | ||||
| 	const struct libipw_geo *geo = libipw_get_geo(priv->ieee); | ||||
| 	struct wireless_dev *wdev = &priv->ieee->wdev; | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 
 | ||||
| 	if (ipw_up(priv)) { | ||||
| 		mutex_unlock(&priv->mutex); | ||||
| 		return -EIO; | ||||
| 		rc = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); | ||||
| 
 | ||||
| 	/* fill-out priv->ieee->bg_band */ | ||||
| 	if (geo->bg_channels) { | ||||
| 		struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; | ||||
| 
 | ||||
| 		bg_band->band = IEEE80211_BAND_2GHZ; | ||||
| 		bg_band->n_channels = geo->bg_channels; | ||||
| 		bg_band->channels = | ||||
| 			kzalloc(geo->bg_channels * | ||||
| 				sizeof(struct ieee80211_channel), GFP_KERNEL); | ||||
| 		/* translate geo->bg to bg_band.channels */ | ||||
| 		for (i = 0; i < geo->bg_channels; i++) { | ||||
| 			bg_band->channels[i].band = IEEE80211_BAND_2GHZ; | ||||
| 			bg_band->channels[i].center_freq = geo->bg[i].freq; | ||||
| 			bg_band->channels[i].hw_value = geo->bg[i].channel; | ||||
| 			bg_band->channels[i].max_power = geo->bg[i].max_power; | ||||
| 			if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) | ||||
| 				bg_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_PASSIVE_SCAN; | ||||
| 			if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) | ||||
| 				bg_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_NO_IBSS; | ||||
| 			if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) | ||||
| 				bg_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_RADAR; | ||||
| 			/* No equivalent for LIBIPW_CH_80211H_RULES,
 | ||||
| 			   LIBIPW_CH_UNIFORM_SPREADING, or | ||||
| 			   LIBIPW_CH_B_ONLY... */ | ||||
| 		} | ||||
| 		/* point at bitrate info */ | ||||
| 		bg_band->bitrates = ipw2200_bg_rates; | ||||
| 		bg_band->n_bitrates = ipw2200_num_bg_rates; | ||||
| 
 | ||||
| 		wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; | ||||
| 	} | ||||
| 
 | ||||
| 	/* fill-out priv->ieee->a_band */ | ||||
| 	if (geo->a_channels) { | ||||
| 		struct ieee80211_supported_band *a_band = &priv->ieee->a_band; | ||||
| 
 | ||||
| 		a_band->band = IEEE80211_BAND_5GHZ; | ||||
| 		a_band->n_channels = geo->a_channels; | ||||
| 		a_band->channels = | ||||
| 			kzalloc(geo->a_channels * | ||||
| 				sizeof(struct ieee80211_channel), GFP_KERNEL); | ||||
| 		/* translate geo->bg to a_band.channels */ | ||||
| 		for (i = 0; i < geo->a_channels; i++) { | ||||
| 			a_band->channels[i].band = IEEE80211_BAND_2GHZ; | ||||
| 			a_band->channels[i].center_freq = geo->a[i].freq; | ||||
| 			a_band->channels[i].hw_value = geo->a[i].channel; | ||||
| 			a_band->channels[i].max_power = geo->a[i].max_power; | ||||
| 			if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) | ||||
| 				a_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_PASSIVE_SCAN; | ||||
| 			if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) | ||||
| 				a_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_NO_IBSS; | ||||
| 			if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) | ||||
| 				a_band->channels[i].flags |= | ||||
| 					IEEE80211_CHAN_RADAR; | ||||
| 			/* No equivalent for LIBIPW_CH_80211H_RULES,
 | ||||
| 			   LIBIPW_CH_UNIFORM_SPREADING, or | ||||
| 			   LIBIPW_CH_B_ONLY... */ | ||||
| 		} | ||||
| 		/* point at bitrate info */ | ||||
| 		a_band->bitrates = ipw2200_a_rates; | ||||
| 		a_band->n_bitrates = ipw2200_num_a_rates; | ||||
| 
 | ||||
| 		wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; | ||||
| 	} | ||||
| 
 | ||||
| 	set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); | ||||
| 
 | ||||
| 	/* With that information in place, we can now register the wiphy... */ | ||||
| 	if (wiphy_register(wdev->wiphy)) { | ||||
| 		rc = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 	return 0; | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /* PCI driver stuff */ | ||||
| @ -11562,7 +11651,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) | ||||
| 	if (priv->prom_net_dev) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv)); | ||||
| 	priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv), 1); | ||||
| 	if (priv->prom_net_dev == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| @ -11581,7 +11670,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) | ||||
| 
 | ||||
| 	rc = register_netdev(priv->prom_net_dev); | ||||
| 	if (rc) { | ||||
| 		free_ieee80211(priv->prom_net_dev); | ||||
| 		free_ieee80211(priv->prom_net_dev, 1); | ||||
| 		priv->prom_net_dev = NULL; | ||||
| 		return rc; | ||||
| 	} | ||||
| @ -11595,7 +11684,7 @@ static void ipw_prom_free(struct ipw_priv *priv) | ||||
| 		return; | ||||
| 
 | ||||
| 	unregister_netdev(priv->prom_net_dev); | ||||
| 	free_ieee80211(priv->prom_net_dev); | ||||
| 	free_ieee80211(priv->prom_net_dev, 1); | ||||
| 
 | ||||
| 	priv->prom_net_dev = NULL; | ||||
| } | ||||
| @ -11623,7 +11712,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, | ||||
| 	struct ipw_priv *priv; | ||||
| 	int i; | ||||
| 
 | ||||
| 	net_dev = alloc_ieee80211(sizeof(struct ipw_priv)); | ||||
| 	net_dev = alloc_ieee80211(sizeof(struct ipw_priv), 0); | ||||
| 	if (net_dev == NULL) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto out; | ||||
| @ -11771,7 +11860,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, | ||||
| 	pci_disable_device(pdev); | ||||
| 	pci_set_drvdata(pdev, NULL); | ||||
|       out_free_ieee80211: | ||||
| 	free_ieee80211(priv->net_dev); | ||||
| 	free_ieee80211(priv->net_dev, 0); | ||||
|       out: | ||||
| 	return err; | ||||
| } | ||||
| @ -11838,7 +11927,11 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev) | ||||
| 	pci_release_regions(pdev); | ||||
| 	pci_disable_device(pdev); | ||||
| 	pci_set_drvdata(pdev, NULL); | ||||
| 	free_ieee80211(priv->net_dev); | ||||
| 	/* wiphy_unregister needs to be here, before free_ieee80211 */ | ||||
| 	wiphy_unregister(priv->ieee->wdev.wiphy); | ||||
| 	kfree(priv->ieee->a_band.channels); | ||||
| 	kfree(priv->ieee->bg_band.channels); | ||||
| 	free_ieee80211(priv->net_dev, 0); | ||||
| 	free_firmware(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -31,6 +31,7 @@ | ||||
| #include <linux/ieee80211.h> | ||||
| 
 | ||||
| #include <net/lib80211.h> | ||||
| #include <net/cfg80211.h> | ||||
| 
 | ||||
| #define LIBIPW_VERSION "git-1.1.13" | ||||
| 
 | ||||
| @ -783,12 +784,15 @@ struct libipw_geo { | ||||
| 
 | ||||
| struct libipw_device { | ||||
| 	struct net_device *dev; | ||||
| 	struct wireless_dev wdev; | ||||
| 	struct libipw_security sec; | ||||
| 
 | ||||
| 	/* Bookkeeping structures */ | ||||
| 	struct libipw_stats ieee_stats; | ||||
| 
 | ||||
| 	struct libipw_geo geo; | ||||
| 	struct ieee80211_supported_band bg_band; | ||||
| 	struct ieee80211_supported_band a_band; | ||||
| 
 | ||||
| 	/* Probe / Beacon management */ | ||||
| 	struct list_head network_free_list; | ||||
| @ -1014,8 +1018,8 @@ static inline int libipw_is_cck_rate(u8 rate) | ||||
| } | ||||
| 
 | ||||
| /* ieee80211.c */ | ||||
| extern void free_ieee80211(struct net_device *dev); | ||||
| extern struct net_device *alloc_ieee80211(int sizeof_priv); | ||||
| extern void free_ieee80211(struct net_device *dev, int monitor); | ||||
| extern struct net_device *alloc_ieee80211(int sizeof_priv, int monitor); | ||||
| extern int libipw_change_mtu(struct net_device *dev, int new_mtu); | ||||
| 
 | ||||
| extern void libipw_networks_age(struct libipw_device *ieee, | ||||
|  | ||||
| @ -62,6 +62,9 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); | ||||
| MODULE_AUTHOR(DRV_COPYRIGHT); | ||||
| MODULE_LICENSE("GPL"); | ||||
| 
 | ||||
| struct cfg80211_ops libipw_config_ops = { }; | ||||
| void *libipw_wiphy_privid = &libipw_wiphy_privid; | ||||
| 
 | ||||
| static int libipw_networks_allocate(struct libipw_device *ieee) | ||||
| { | ||||
| 	if (ieee->networks) | ||||
| @ -140,7 +143,7 @@ int libipw_change_mtu(struct net_device *dev, int new_mtu) | ||||
| } | ||||
| EXPORT_SYMBOL(libipw_change_mtu); | ||||
| 
 | ||||
| struct net_device *alloc_ieee80211(int sizeof_priv) | ||||
| struct net_device *alloc_ieee80211(int sizeof_priv, int monitor) | ||||
| { | ||||
| 	struct libipw_device *ieee; | ||||
| 	struct net_device *dev; | ||||
| @ -157,10 +160,31 @@ struct net_device *alloc_ieee80211(int sizeof_priv) | ||||
| 
 | ||||
| 	ieee->dev = dev; | ||||
| 
 | ||||
| 	if (!monitor) { | ||||
| 		ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); | ||||
| 		if (!ieee->wdev.wiphy) { | ||||
| 			LIBIPW_ERROR("Unable to allocate wiphy.\n"); | ||||
| 			goto failed_free_netdev; | ||||
| 		} | ||||
| 
 | ||||
| 		ieee->dev->ieee80211_ptr = &ieee->wdev; | ||||
| 		ieee->wdev.iftype = NL80211_IFTYPE_STATION; | ||||
| 
 | ||||
| 		/* Fill-out wiphy structure bits we know...  Not enough info
 | ||||
| 		   here to call set_wiphy_dev or set MAC address or channel info | ||||
| 		   -- have to do that in ->ndo_init... */ | ||||
| 		ieee->wdev.wiphy->privid = libipw_wiphy_privid; | ||||
| 
 | ||||
| 		ieee->wdev.wiphy->max_scan_ssids = 1; | ||||
| 		ieee->wdev.wiphy->max_scan_ie_len = 0; | ||||
| 		ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | ||||
| 						| BIT(NL80211_IFTYPE_ADHOC); | ||||
| 	} | ||||
| 
 | ||||
| 	err = libipw_networks_allocate(ieee); | ||||
| 	if (err) { | ||||
| 		LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); | ||||
| 		goto failed_free_netdev; | ||||
| 		goto failed_free_wiphy; | ||||
| 	} | ||||
| 	libipw_networks_initialize(ieee); | ||||
| 
 | ||||
| @ -193,19 +217,27 @@ struct net_device *alloc_ieee80211(int sizeof_priv) | ||||
| 
 | ||||
| 	return dev; | ||||
| 
 | ||||
| failed_free_wiphy: | ||||
| 	if (!monitor) | ||||
| 		wiphy_free(ieee->wdev.wiphy); | ||||
| failed_free_netdev: | ||||
| 	free_netdev(dev); | ||||
| failed: | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| void free_ieee80211(struct net_device *dev) | ||||
| void free_ieee80211(struct net_device *dev, int monitor) | ||||
| { | ||||
| 	struct libipw_device *ieee = netdev_priv(dev); | ||||
| 
 | ||||
| 	lib80211_crypt_info_free(&ieee->crypt_info); | ||||
| 
 | ||||
| 	libipw_networks_free(ieee); | ||||
| 
 | ||||
| 	/* free cfg80211 resources */ | ||||
| 	if (!monitor) | ||||
| 		wiphy_free(ieee->wdev.wiphy); | ||||
| 
 | ||||
| 	free_netdev(dev); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -173,6 +173,7 @@ struct iwl_cfg iwl1000_bgn_cfg = { | ||||
| 	.use_rts_for_ht = true, /* use rts/cts protection */ | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| 	.support_ct_kill_exit = true, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl1000_bg_cfg = { | ||||
|  | ||||
| @ -221,22 +221,13 @@ struct iwl3945_ibss_seq { | ||||
|  * for use by iwl-*.c | ||||
|  * | ||||
|  *****************************************************************************/ | ||||
| extern int iwl3945_power_init_handle(struct iwl_priv *priv); | ||||
| extern int iwl3945_eeprom_init(struct iwl_priv *priv); | ||||
| extern int iwl3945_calc_db_from_ratio(int sig_ratio); | ||||
| extern int iwl3945_calc_sig_qual(int rssi_dbm, int noise_dbm); | ||||
| extern int iwl3945_tx_queue_init(struct iwl_priv *priv, | ||||
| 			     struct iwl_tx_queue *txq, int count, u32 id); | ||||
| extern void iwl3945_rx_replenish(void *data); | ||||
| extern void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); | ||||
| extern void iwl3945_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq); | ||||
| extern int iwl3945_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, | ||||
| 			    const void *data); | ||||
| extern int __must_check iwl3945_send_cmd(struct iwl_priv *priv, | ||||
| 					 struct iwl_host_cmd *cmd); | ||||
| extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv, | ||||
| 					struct ieee80211_hdr *hdr,int left); | ||||
| extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv); | ||||
| extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log); | ||||
| extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv); | ||||
| 
 | ||||
| /*
 | ||||
|  | ||||
| @ -1818,8 +1818,9 @@ static u16 iwl4965_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) | ||||
| 	addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; | ||||
| 	addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; | ||||
| 	addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; | ||||
| 	addsta->sleep_tx_count = cmd->sleep_tx_count; | ||||
| 	addsta->reserved1 = cpu_to_le16(0); | ||||
| 	addsta->reserved2 = cpu_to_le32(0); | ||||
| 	addsta->reserved2 = cpu_to_le16(0); | ||||
| 
 | ||||
| 	return (u16)sizeof(struct iwl4965_addsta_cmd); | ||||
| } | ||||
| @ -1865,8 +1866,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, | ||||
| 		info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]); | ||||
| 		info->status.rates[0].count = tx_resp->failure_frame + 1; | ||||
| 		info->flags &= ~IEEE80211_TX_CTL_AMPDU; | ||||
| 		info->flags |= iwl_is_tx_success(status) ? | ||||
| 			IEEE80211_TX_STAT_ACK : 0; | ||||
| 		info->flags |= iwl_tx_status_to_mac80211(status); | ||||
| 		iwl_hwrate_to_tx_control(priv, rate_n_flags, info); | ||||
| 		/* FIXME: code repetition end */ | ||||
| 
 | ||||
| @ -2021,8 +2021,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, | ||||
| 		} | ||||
| 	} else { | ||||
| 		info->status.rates[0].count = tx_resp->failure_frame + 1; | ||||
| 		info->flags |= iwl_is_tx_success(status) ? | ||||
| 					IEEE80211_TX_STAT_ACK : 0; | ||||
| 		info->flags |= iwl_tx_status_to_mac80211(status); | ||||
| 		iwl_hwrate_to_tx_control(priv, | ||||
| 					le32_to_cpu(tx_resp->rate_n_flags), | ||||
| 					info); | ||||
| @ -2240,6 +2239,7 @@ struct iwl_cfg iwl4965_agn_cfg = { | ||||
| 	.broken_powersave = true, | ||||
| 	.led_compensation = 61, | ||||
| 	.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| /* Module firmware */ | ||||
|  | ||||
| @ -994,8 +994,7 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, | ||||
| 		info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]); | ||||
| 		info->status.rates[0].count = tx_resp->failure_frame + 1; | ||||
| 		info->flags &= ~IEEE80211_TX_CTL_AMPDU; | ||||
| 		info->flags |= iwl_is_tx_success(status) ? | ||||
| 					IEEE80211_TX_STAT_ACK : 0; | ||||
| 		info->flags |= iwl_tx_status_to_mac80211(status); | ||||
| 		iwl_hwrate_to_tx_control(priv, rate_n_flags, info); | ||||
| 
 | ||||
| 		/* FIXME: code repetition end */ | ||||
| @ -1140,8 +1139,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, | ||||
| 		BUG_ON(txq_id != txq->swq_id); | ||||
| 
 | ||||
| 		info->status.rates[0].count = tx_resp->failure_frame + 1; | ||||
| 		info->flags |= iwl_is_tx_success(status) ? | ||||
| 					IEEE80211_TX_STAT_ACK : 0; | ||||
| 		info->flags |= iwl_tx_status_to_mac80211(status); | ||||
| 		iwl_hwrate_to_tx_control(priv, | ||||
| 					le32_to_cpu(tx_resp->rate_n_flags), | ||||
| 					info); | ||||
| @ -1251,6 +1249,22 @@ int  iwl5000_send_tx_power(struct iwl_priv *priv) | ||||
| 
 | ||||
| 	/* half dBm need to multiply */ | ||||
| 	tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); | ||||
| 
 | ||||
| 	if (priv->tx_power_lmt_in_half_dbm && | ||||
| 	    priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) { | ||||
| 		/*
 | ||||
| 		 * For the newer devices which using enhanced/extend tx power | ||||
| 		 * table in EEPROM, the format is in half dBm. driver need to | ||||
| 		 * convert to dBm format before report to mac80211. | ||||
| 		 * By doing so, there is a possibility of 1/2 dBm resolution | ||||
| 		 * lost. driver will perform "round-up" operation before | ||||
| 		 * reporting, but it will cause 1/2 dBm tx power over the | ||||
| 		 * regulatory limit. Perform the checking here, if the | ||||
| 		 * "tx_power_user_lmt" is higher than EEPROM value (in | ||||
| 		 * half-dBm format), lower the tx power based on EEPROM | ||||
| 		 */ | ||||
| 		tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm; | ||||
| 	} | ||||
| 	tx_power_cmd.flags = IWL50_TX_POWER_NO_CLOSED; | ||||
| 	tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO; | ||||
| 
 | ||||
| @ -1584,14 +1598,15 @@ struct iwl_cfg iwl5300_agn_cfg = { | ||||
| 	.ht_greenfield_support = true, | ||||
| 	.led_compensation = 51, | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl5100_bg_cfg = { | ||||
| 	.name = "5100BG", | ||||
| struct iwl_cfg iwl5100_bgn_cfg = { | ||||
| 	.name = "5100BGN", | ||||
| 	.fw_name_pre = IWL5000_FW_PRE, | ||||
| 	.ucode_api_max = IWL5000_UCODE_API_MAX, | ||||
| 	.ucode_api_min = IWL5000_UCODE_API_MIN, | ||||
| 	.sku = IWL_SKU_G, | ||||
| 	.sku = IWL_SKU_G|IWL_SKU_N, | ||||
| 	.ops = &iwl5000_ops, | ||||
| 	.eeprom_size = IWL_5000_EEPROM_IMG_SIZE, | ||||
| 	.eeprom_ver = EEPROM_5000_EEPROM_VERSION, | ||||
| @ -1627,7 +1642,6 @@ struct iwl_cfg iwl5100_abg_cfg = { | ||||
| 	.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, | ||||
| 	.set_l0s = true, | ||||
| 	.use_bsm = false, | ||||
| 	.ht_greenfield_support = true, | ||||
| 	.led_compensation = 51, | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| }; | ||||
| @ -1653,6 +1667,7 @@ struct iwl_cfg iwl5100_agn_cfg = { | ||||
| 	.ht_greenfield_support = true, | ||||
| 	.led_compensation = 51, | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl5350_agn_cfg = { | ||||
| @ -1676,6 +1691,7 @@ struct iwl_cfg iwl5350_agn_cfg = { | ||||
| 	.ht_greenfield_support = true, | ||||
| 	.led_compensation = 51, | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl5150_agn_cfg = { | ||||
| @ -1699,6 +1715,29 @@ struct iwl_cfg iwl5150_agn_cfg = { | ||||
| 	.ht_greenfield_support = true, | ||||
| 	.led_compensation = 51, | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl5150_abg_cfg = { | ||||
| 	.name = "5150ABG", | ||||
| 	.fw_name_pre = IWL5150_FW_PRE, | ||||
| 	.ucode_api_max = IWL5150_UCODE_API_MAX, | ||||
| 	.ucode_api_min = IWL5150_UCODE_API_MIN, | ||||
| 	.sku = IWL_SKU_A|IWL_SKU_G, | ||||
| 	.ops = &iwl5150_ops, | ||||
| 	.eeprom_size = IWL_5000_EEPROM_IMG_SIZE, | ||||
| 	.eeprom_ver = EEPROM_5050_EEPROM_VERSION, | ||||
| 	.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, | ||||
| 	.num_of_queues = IWL50_NUM_QUEUES, | ||||
| 	.num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, | ||||
| 	.mod_params = &iwl50_mod_params, | ||||
| 	.valid_tx_ant = ANT_A, | ||||
| 	.valid_rx_ant = ANT_AB, | ||||
| 	.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, | ||||
| 	.set_l0s = true, | ||||
| 	.use_bsm = false, | ||||
| 	.led_compensation = 51, | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| }; | ||||
| 
 | ||||
| MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); | ||||
|  | ||||
| @ -306,6 +306,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = { | ||||
| 	.supports_idle = true, | ||||
| 	.adv_thermal_throttle = true, | ||||
| 	.support_ct_kill_exit = true, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl6000i_2abg_cfg = { | ||||
| @ -394,8 +395,7 @@ struct iwl_cfg iwl6050_2agn_cfg = { | ||||
| 	.supports_idle = true, | ||||
| 	.adv_thermal_throttle = true, | ||||
| 	.support_ct_kill_exit = true, | ||||
| 	.support_sm_ps = true, | ||||
| 	.support_wimax_coexist = true, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DYNAMIC, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl6050_2abg_cfg = { | ||||
| @ -425,7 +425,6 @@ struct iwl_cfg iwl6050_2abg_cfg = { | ||||
| 	.supports_idle = true, | ||||
| 	.adv_thermal_throttle = true, | ||||
| 	.support_ct_kill_exit = true, | ||||
| 	.support_wimax_coexist = true, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl6000_3agn_cfg = { | ||||
| @ -456,38 +455,7 @@ struct iwl_cfg iwl6000_3agn_cfg = { | ||||
| 	.supports_idle = true, | ||||
| 	.adv_thermal_throttle = true, | ||||
| 	.support_ct_kill_exit = true, | ||||
| }; | ||||
| 
 | ||||
| struct iwl_cfg iwl6050_3agn_cfg = { | ||||
| 	.name = "6050 Series 3x3 AGN", | ||||
| 	.fw_name_pre = IWL6050_FW_PRE, | ||||
| 	.ucode_api_max = IWL6050_UCODE_API_MAX, | ||||
| 	.ucode_api_min = IWL6050_UCODE_API_MIN, | ||||
| 	.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, | ||||
| 	.ops = &iwl6050_ops, | ||||
| 	.eeprom_size = OTP_LOW_IMAGE_SIZE, | ||||
| 	.eeprom_ver = EEPROM_6050_EEPROM_VERSION, | ||||
| 	.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, | ||||
| 	.num_of_queues = IWL50_NUM_QUEUES, | ||||
| 	.num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES, | ||||
| 	.mod_params = &iwl50_mod_params, | ||||
| 	.valid_tx_ant = ANT_ABC, | ||||
| 	.valid_rx_ant = ANT_ABC, | ||||
| 	.pll_cfg_val = 0, | ||||
| 	.set_l0s = true, | ||||
| 	.use_bsm = false, | ||||
| 	.pa_type = IWL_PA_SYSTEM, | ||||
| 	.max_ll_items = OTP_MAX_LL_ITEMS_6x50, | ||||
| 	.shadow_ram_support = true, | ||||
| 	.ht_greenfield_support = true, | ||||
| 	.led_compensation = 51, | ||||
| 	.use_rts_for_ht = true, /* use rts/cts protection */ | ||||
| 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, | ||||
| 	.supports_idle = true, | ||||
| 	.adv_thermal_throttle = true, | ||||
| 	.support_ct_kill_exit = true, | ||||
| 	.support_sm_ps = true, | ||||
| 	.support_wimax_coexist = true, | ||||
| 	.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED, | ||||
| }; | ||||
| 
 | ||||
| MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); | ||||
|  | ||||
| @ -301,7 +301,7 @@ static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, | ||||
| 	if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { | ||||
| 		IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", | ||||
| 				sta->addr, tid); | ||||
| 		ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid); | ||||
| 		ieee80211_start_tx_ba_session(sta, tid); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -2964,16 +2964,16 @@ static void rs_add_debugfs(void *priv, void *priv_sta, | ||||
| { | ||||
| 	struct iwl_lq_sta *lq_sta = priv_sta; | ||||
| 	lq_sta->rs_sta_dbgfs_scale_table_file = | ||||
| 		debugfs_create_file("rate_scale_table", 0600, dir, | ||||
| 		debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, | ||||
| 				lq_sta, &rs_sta_dbgfs_scale_table_ops); | ||||
| 	lq_sta->rs_sta_dbgfs_stats_table_file = | ||||
| 		debugfs_create_file("rate_stats_table", 0600, dir, | ||||
| 		debugfs_create_file("rate_stats_table", S_IRUSR, dir, | ||||
| 			lq_sta, &rs_sta_dbgfs_stats_table_ops); | ||||
| 	lq_sta->rs_sta_dbgfs_rate_scale_data_file = | ||||
| 		debugfs_create_file("rate_scale_data", 0600, dir, | ||||
| 		debugfs_create_file("rate_scale_data", S_IRUSR, dir, | ||||
| 			lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); | ||||
| 	lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = | ||||
| 		debugfs_create_u8("tx_agg_tid_enable", 0600, dir, | ||||
| 		debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, | ||||
| 		&lq_sta->tx_agg_tid_en); | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -311,7 +311,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) | ||||
| 	list_add(&frame->list, &priv->free_frames); | ||||
| } | ||||
| 
 | ||||
| static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, | ||||
| static u32 iwl_fill_beacon_frame(struct iwl_priv *priv, | ||||
| 					  struct ieee80211_hdr *hdr, | ||||
| 					  int left) | ||||
| { | ||||
| @ -328,34 +328,74 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, | ||||
| 	return priv->ibss_beacon->len; | ||||
| } | ||||
| 
 | ||||
| /* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ | ||||
| static void iwl_set_beacon_tim(struct iwl_priv *priv, | ||||
| 		struct iwl_tx_beacon_cmd *tx_beacon_cmd, | ||||
| 		u8 *beacon, u32 frame_size) | ||||
| { | ||||
| 	u16 tim_idx; | ||||
| 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The index is relative to frame start but we start looking at the | ||||
| 	 * variable-length part of the beacon. | ||||
| 	 */ | ||||
| 	tim_idx = mgmt->u.beacon.variable - beacon; | ||||
| 
 | ||||
| 	/* Parse variable-length elements of beacon to find WLAN_EID_TIM */ | ||||
| 	while ((tim_idx < (frame_size - 2)) && | ||||
| 			(beacon[tim_idx] != WLAN_EID_TIM)) | ||||
| 		tim_idx += beacon[tim_idx+1] + 2; | ||||
| 
 | ||||
| 	/* If TIM field was found, set variables */ | ||||
| 	if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { | ||||
| 		tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); | ||||
| 		tx_beacon_cmd->tim_size = beacon[tim_idx+1]; | ||||
| 	} else | ||||
| 		IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); | ||||
| } | ||||
| 
 | ||||
| static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv, | ||||
| 				       struct iwl_frame *frame, u8 rate) | ||||
| 				       struct iwl_frame *frame) | ||||
| { | ||||
| 	struct iwl_tx_beacon_cmd *tx_beacon_cmd; | ||||
| 	unsigned int frame_size; | ||||
| 	u32 frame_size; | ||||
| 	u32 rate_flags; | ||||
| 	u32 rate; | ||||
| 	/*
 | ||||
| 	 * We have to set up the TX command, the TX Beacon command, and the | ||||
| 	 * beacon contents. | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* Initialize memory */ | ||||
| 	tx_beacon_cmd = &frame->u.beacon; | ||||
| 	memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); | ||||
| 
 | ||||
| 	tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; | ||||
| 	tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; | ||||
| 
 | ||||
| 	/* Set up TX beacon contents */ | ||||
| 	frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame, | ||||
| 				sizeof(frame->u) - sizeof(*tx_beacon_cmd)); | ||||
| 	if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	BUG_ON(frame_size > MAX_MPDU_SIZE); | ||||
| 	/* Set up TX command fields */ | ||||
| 	tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); | ||||
| 
 | ||||
| 	if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) | ||||
| 		tx_beacon_cmd->tx.rate_n_flags = | ||||
| 			iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); | ||||
| 	else | ||||
| 		tx_beacon_cmd->tx.rate_n_flags = | ||||
| 			iwl_hw_set_rate_n_flags(rate, 0); | ||||
| 
 | ||||
| 	tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; | ||||
| 	tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; | ||||
| 	tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | | ||||
| 				     TX_CMD_FLG_TSF_MSK | | ||||
| 				     TX_CMD_FLG_STA_RATE_MSK; | ||||
| 		TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; | ||||
| 
 | ||||
| 	/* Set up TX beacon command fields */ | ||||
| 	iwl_set_beacon_tim(priv, tx_beacon_cmd, (u8 *)tx_beacon_cmd->frame, | ||||
| 			frame_size); | ||||
| 
 | ||||
| 	/* Set up packet rate and flags */ | ||||
| 	rate = iwl_rate_get_lowest_plcp(priv); | ||||
| 	priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant); | ||||
| 	rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); | ||||
| 	if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE)) | ||||
| 		rate_flags |= RATE_MCS_CCK_MSK; | ||||
| 	tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate, | ||||
| 			rate_flags); | ||||
| 
 | ||||
| 	return sizeof(*tx_beacon_cmd) + frame_size; | ||||
| } | ||||
| @ -364,19 +404,20 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv) | ||||
| 	struct iwl_frame *frame; | ||||
| 	unsigned int frame_size; | ||||
| 	int rc; | ||||
| 	u8 rate; | ||||
| 
 | ||||
| 	frame = iwl_get_free_frame(priv); | ||||
| 
 | ||||
| 	if (!frame) { | ||||
| 		IWL_ERR(priv, "Could not obtain free frame buffer for beacon " | ||||
| 			  "command.\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	rate = iwl_rate_get_lowest_plcp(priv); | ||||
| 
 | ||||
| 	frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate); | ||||
| 	frame_size = iwl_hw_get_beacon_cmd(priv, frame); | ||||
| 	if (!frame_size) { | ||||
| 		IWL_ERR(priv, "Error configuring the beacon command\n"); | ||||
| 		iwl_free_frame(priv, frame); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, | ||||
| 			      &frame->u.cmd[0]); | ||||
| @ -613,7 +654,7 @@ static void iwl_bg_statistics_periodic(unsigned long data) | ||||
| 	if (!iwl_is_ready_rf(priv)) | ||||
| 		return; | ||||
| 
 | ||||
| 	iwl_send_statistics_request(priv, CMD_ASYNC); | ||||
| 	iwl_send_statistics_request(priv, CMD_ASYNC, false); | ||||
| } | ||||
| 
 | ||||
| static void iwl_rx_beacon_notif(struct iwl_priv *priv, | ||||
| @ -730,7 +771,7 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv) | ||||
| 	 * statistics request from the host as well as for the periodic | ||||
| 	 * statistics notifications (after received beacons) from the uCode. | ||||
| 	 */ | ||||
| 	priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics; | ||||
| 	priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_reply_statistics; | ||||
| 	priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics; | ||||
| 
 | ||||
| 	iwl_setup_spectrum_handlers(priv); | ||||
| @ -1038,7 +1079,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) | ||||
| 	if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { | ||||
| 		iwl_rx_handle(priv); | ||||
| 		priv->isr_stats.rx++; | ||||
| 		iwl_leds_background(priv); | ||||
| 		handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); | ||||
| 	} | ||||
| 
 | ||||
| @ -1226,19 +1266,27 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) | ||||
| 		 * 3- update RX shared data to indicate last write index. | ||||
| 		 * 4- send interrupt. | ||||
| 		 * This could lead to RX race, driver could receive RX interrupt | ||||
| 		 * but the shared data changes does not reflect this. | ||||
| 		 * this could lead to RX race, RX periodic will solve this race | ||||
| 		 * but the shared data changes does not reflect this; | ||||
| 		 * periodic interrupt will detect any dangling Rx activity. | ||||
| 		 */ | ||||
| 		iwl_write32(priv, CSR_INT_PERIODIC_REG, | ||||
| 
 | ||||
| 		/* Disable periodic interrupt; we use it as just a one-shot. */ | ||||
| 		iwl_write8(priv, CSR_INT_PERIODIC_REG, | ||||
| 			    CSR_INT_PERIODIC_DIS); | ||||
| 		iwl_rx_handle(priv); | ||||
| 		/* Only set RX periodic if real RX is received. */ | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Enable periodic interrupt in 8 msec only if we received | ||||
| 		 * real RX interrupt (instead of just periodic int), to catch | ||||
| 		 * any dangling Rx interrupt.  If it was just the periodic | ||||
| 		 * interrupt, there was no dangling Rx activity, and no need | ||||
| 		 * to extend the periodic interrupt; one-shot is enough. | ||||
| 		 */ | ||||
| 		if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) | ||||
| 			iwl_write32(priv, CSR_INT_PERIODIC_REG, | ||||
| 			iwl_write8(priv, CSR_INT_PERIODIC_REG, | ||||
| 				    CSR_INT_PERIODIC_ENA); | ||||
| 
 | ||||
| 		priv->isr_stats.rx++; | ||||
| 		iwl_leds_background(priv); | ||||
| 	} | ||||
| 
 | ||||
| 	/* This "Tx" DMA channel is used only for loading uCode */ | ||||
| @ -1557,7 +1605,6 @@ static int iwl_read_ucode(struct iwl_priv *priv) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| static const char *desc_lookup_text[] = { | ||||
| 	"OK", | ||||
| 	"FAIL", | ||||
| @ -1710,10 +1757,42 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, | ||||
| 	spin_unlock_irqrestore(&priv->reg_lock, reg_flags); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * iwl_print_last_event_logs - Dump the newest # of event log to syslog | ||||
|  */ | ||||
| static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, | ||||
| 				      u32 num_wraps, u32 next_entry, | ||||
| 				      u32 size, u32 mode) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * display the newest DEFAULT_LOG_ENTRIES entries | ||||
| 	 * i.e the entries just before the next ont that uCode would fill. | ||||
| 	 */ | ||||
| 	if (num_wraps) { | ||||
| 		if (next_entry < size) { | ||||
| 			iwl_print_event_log(priv, | ||||
| 					capacity - (size - next_entry), | ||||
| 					size - next_entry, mode); | ||||
| 			iwl_print_event_log(priv, 0, | ||||
| 				    next_entry, mode); | ||||
| 		} else | ||||
| 			iwl_print_event_log(priv, next_entry - size, | ||||
| 				    size, mode); | ||||
| 	} else { | ||||
| 		if (next_entry < size) | ||||
| 			iwl_print_event_log(priv, 0, next_entry, mode); | ||||
| 		else | ||||
| 			iwl_print_event_log(priv, next_entry - size, | ||||
| 					    size, mode); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* For sanity check only.  Actual size is determined by uCode, typ. 512 */ | ||||
| #define MAX_EVENT_LOG_SIZE (512) | ||||
| 
 | ||||
| void iwl_dump_nic_event_log(struct iwl_priv *priv) | ||||
| #define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) | ||||
| 
 | ||||
| void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log) | ||||
| { | ||||
| 	u32 base;       /* SRAM byte address of event log header */ | ||||
| 	u32 capacity;   /* event log capacity in # entries */ | ||||
| @ -1758,19 +1837,37 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv) | ||||
| 		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); | ||||
| 
 | ||||
| } | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| 	if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)) | ||||
| 		size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) | ||||
| 			? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; | ||||
| #else | ||||
| 	size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) | ||||
| 		? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; | ||||
| #endif | ||||
| 	IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", | ||||
| 		size); | ||||
| 
 | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| 	if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { | ||||
| 		/*
 | ||||
| 		 * 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); | ||||
| 	} else | ||||
| 		iwl_print_last_event_logs(priv, capacity, num_wraps, | ||||
| 					next_entry, size, mode); | ||||
| #else | ||||
| 	iwl_print_last_event_logs(priv, capacity, num_wraps, | ||||
| 				next_entry, size, mode); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * iwl_alive_start - called after REPLY_ALIVE notification received | ||||
| @ -2360,16 +2457,14 @@ static int iwl_setup_mac(struct iwl_priv *priv) | ||||
| 		BIT(NL80211_IFTYPE_STATION) | | ||||
| 		BIT(NL80211_IFTYPE_ADHOC); | ||||
| 
 | ||||
| 	hw->wiphy->custom_regulatory = true; | ||||
| 
 | ||||
| 	/* Firmware does not support this */ | ||||
| 	hw->wiphy->disable_beacon_hints = true; | ||||
| 	hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY | | ||||
| 			    WIPHY_FLAG_DISABLE_BEACON_HINTS; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * For now, disable PS by default because it affects | ||||
| 	 * RX performance significantly. | ||||
| 	 */ | ||||
| 	hw->wiphy->ps_default = false; | ||||
| 	hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; | ||||
| 
 | ||||
| 	hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; | ||||
| 	/* we create the 802.11 header and a zero-length SSID element */ | ||||
| @ -2523,6 +2618,10 @@ void iwl_config_ap(struct iwl_priv *priv) | ||||
| 			IWL_WARN(priv, "REPLY_RXON_TIMING failed - " | ||||
| 					"Attempting to continue.\n"); | ||||
| 
 | ||||
| 		/* AP has all antennas */ | ||||
| 		priv->chain_noise_data.active_chains = | ||||
| 			priv->hw_params.valid_rx_ant; | ||||
| 		iwl_set_rxon_ht(priv, &priv->current_ht_config); | ||||
| 		if (priv->cfg->ops->hcmd->set_rxon_chain) | ||||
| 			priv->cfg->ops->hcmd->set_rxon_chain(priv); | ||||
| 
 | ||||
| @ -2551,6 +2650,7 @@ void iwl_config_ap(struct iwl_priv *priv) | ||||
| 		/* restore RXON assoc */ | ||||
| 		priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; | ||||
| 		iwlcore_commit_rxon(priv); | ||||
| 		iwl_reset_qos(priv); | ||||
| 		spin_lock_irqsave(&priv->lock, flags); | ||||
| 		iwl_activate_qos(priv, 1); | ||||
| 		spin_unlock_irqrestore(&priv->lock, flags); | ||||
| @ -2646,6 +2746,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, | ||||
| } | ||||
| 
 | ||||
| static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, | ||||
| 				struct ieee80211_vif *vif, | ||||
| 			     enum ieee80211_ampdu_mlme_action action, | ||||
| 			     struct ieee80211_sta *sta, u16 tid, u16 *ssn) | ||||
| { | ||||
| @ -2699,6 +2800,45 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void iwl_mac_sta_notify(struct ieee80211_hw *hw, | ||||
| 			       struct ieee80211_vif *vif, | ||||
| 			       enum sta_notify_cmd cmd, | ||||
| 			       struct ieee80211_sta *sta) | ||||
| { | ||||
| 	struct iwl_priv *priv = hw->priv; | ||||
| 	struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; | ||||
| 	int sta_id; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * TODO: We really should use this callback to | ||||
| 	 *	 actually maintain the station table in | ||||
| 	 *	 the device. | ||||
| 	 */ | ||||
| 
 | ||||
| 	switch (cmd) { | ||||
| 	case STA_NOTIFY_ADD: | ||||
| 		atomic_set(&sta_priv->pending_frames, 0); | ||||
| 		if (vif->type == NL80211_IFTYPE_AP) | ||||
| 			sta_priv->client = true; | ||||
| 		break; | ||||
| 	case STA_NOTIFY_SLEEP: | ||||
| 		WARN_ON(!sta_priv->client); | ||||
| 		sta_priv->asleep = true; | ||||
| 		if (atomic_read(&sta_priv->pending_frames) > 0) | ||||
| 			ieee80211_sta_block_awake(hw, sta, true); | ||||
| 		break; | ||||
| 	case STA_NOTIFY_AWAKE: | ||||
| 		WARN_ON(!sta_priv->client); | ||||
| 		sta_priv->asleep = false; | ||||
| 		sta_id = iwl_find_station(priv, sta->addr); | ||||
| 		if (sta_id != IWL_INVALID_STATION) | ||||
| 			iwl_sta_modify_ps_wake(priv, sta_id); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*****************************************************************************
 | ||||
|  * | ||||
|  * sysfs attributes | ||||
| @ -2893,7 +3033,7 @@ static ssize_t show_statistics(struct device *d, | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	rc = iwl_send_statistics_request(priv, 0); | ||||
| 	rc = iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	if (rc) { | ||||
| @ -3045,10 +3185,6 @@ static int iwl_init_drv(struct iwl_priv *priv) | ||||
| 	priv->band = IEEE80211_BAND_2GHZ; | ||||
| 
 | ||||
| 	priv->iw_mode = NL80211_IFTYPE_STATION; | ||||
| 	if (priv->cfg->support_sm_ps) | ||||
| 		priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DYNAMIC; | ||||
| 	else | ||||
| 		priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED; | ||||
| 
 | ||||
| 	/* Choose which receivers/antennas to use */ | ||||
| 	if (priv->cfg->ops->hcmd->set_rxon_chain) | ||||
| @ -3130,7 +3266,8 @@ static struct ieee80211_ops iwl_hw_ops = { | ||||
| 	.reset_tsf = iwl_mac_reset_tsf, | ||||
| 	.bss_info_changed = iwl_bss_info_changed, | ||||
| 	.ampdu_action = iwl_mac_ampdu_action, | ||||
| 	.hw_scan = iwl_mac_hw_scan | ||||
| 	.hw_scan = iwl_mac_hw_scan, | ||||
| 	.sta_notify = iwl_mac_sta_notify, | ||||
| }; | ||||
| 
 | ||||
| static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||||
| @ -3454,23 +3591,63 @@ static struct pci_device_id iwl_hw_card_ids[] = { | ||||
| 	{IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)}, | ||||
| #endif /* CONFIG_IWL4965 */ | ||||
| #ifdef CONFIG_IWL5000 | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4232, PCI_ANY_ID, iwl5100_agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4235, PCI_ANY_ID, iwl5300_agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4236, PCI_ANY_ID, iwl5300_agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x4237, PCI_ANY_ID, iwl5100_agn_cfg)}, | ||||
| /* 5350 WiFi/WiMax */ | ||||
| 	{IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, | ||||
| /* 5150 Wifi/WiMax */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)}, | ||||
| /* 5100 Series WiFi */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ | ||||
| 
 | ||||
| /* 5300 Series WiFi */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ | ||||
| 
 | ||||
| /* 5350 Series WiFi/WiMax */ | ||||
| 	{IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ | ||||
| 
 | ||||
| /* 5150 Series Wifi/WiMax */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ | ||||
| 
 | ||||
| 	{IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ | ||||
| 	{IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ | ||||
| 
 | ||||
| /* 6x00 Series */ | ||||
| 	{IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, | ||||
| @ -3485,13 +3662,10 @@ static struct pci_device_id iwl_hw_card_ids[] = { | ||||
| 	{IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, | ||||
| 
 | ||||
| /* 6x50 WiFi/WiMax Series */ | ||||
| 	{IWL_PCI_DEVICE(0x0086, 0x1101, iwl6050_3agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0086, 0x1121, iwl6050_3agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0088, 0x1111, iwl6050_3agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, | ||||
| 	{IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, | ||||
| 
 | ||||
|  | ||||
| @ -900,7 +900,7 @@ void iwl_reset_run_time_calib(struct iwl_priv *priv) | ||||
| 
 | ||||
| 	/* Ask for statistics now, the uCode will send notification
 | ||||
| 	 * periodically after association */ | ||||
| 	iwl_send_statistics_request(priv, CMD_ASYNC); | ||||
| 	iwl_send_statistics_request(priv, CMD_ASYNC, true); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_reset_run_time_calib); | ||||
| 
 | ||||
|  | ||||
| @ -977,6 +977,7 @@ struct iwl_qosparam_cmd { | ||||
| #define	STA_MODIFY_TX_RATE_MSK		0x04 | ||||
| #define STA_MODIFY_ADDBA_TID_MSK	0x08 | ||||
| #define STA_MODIFY_DELBA_TID_MSK	0x10 | ||||
| #define STA_MODIFY_SLEEP_TX_COUNT_MSK	0x20 | ||||
| 
 | ||||
| /* Receiver address (actually, Rx station's index into station table),
 | ||||
|  * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ | ||||
| @ -1107,7 +1108,14 @@ struct iwl4965_addsta_cmd { | ||||
| 	 * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ | ||||
| 	__le16 add_immediate_ba_ssn; | ||||
| 
 | ||||
| 	__le32 reserved2; | ||||
| 	/*
 | ||||
| 	 * Number of packets OK to transmit to station even though | ||||
| 	 * it is asleep -- used to synchronise PS-poll and u-APSD | ||||
| 	 * responses while ucode keeps track of STA sleep state. | ||||
| 	 */ | ||||
| 	__le16 sleep_tx_count; | ||||
| 
 | ||||
| 	__le16 reserved2; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /* 5000 */ | ||||
| @ -1138,7 +1146,14 @@ struct iwl_addsta_cmd { | ||||
| 	 * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ | ||||
| 	__le16 add_immediate_ba_ssn; | ||||
| 
 | ||||
| 	__le32 reserved2; | ||||
| 	/*
 | ||||
| 	 * Number of packets OK to transmit to station even though | ||||
| 	 * it is asleep -- used to synchronise PS-poll and u-APSD | ||||
| 	 * responses while ucode keeps track of STA sleep state. | ||||
| 	 */ | ||||
| 	__le16 sleep_tx_count; | ||||
| 
 | ||||
| 	__le16 reserved2; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| @ -1690,6 +1705,21 @@ enum { | ||||
| 	TX_ABORT_REQUIRED_MSK = 0x80000000,	/* bits 31:31 */ | ||||
| }; | ||||
| 
 | ||||
| static inline u32 iwl_tx_status_to_mac80211(u32 status) | ||||
| { | ||||
| 	status &= TX_STATUS_MSK; | ||||
| 
 | ||||
| 	switch (status) { | ||||
| 	case TX_STATUS_SUCCESS: | ||||
| 	case TX_STATUS_DIRECT_DONE: | ||||
| 		return IEEE80211_TX_STAT_ACK; | ||||
| 	case TX_STATUS_FAIL_DEST_PS: | ||||
| 		return IEEE80211_TX_STAT_TX_FILTERED; | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline bool iwl_is_tx_success(u32 status) | ||||
| { | ||||
| 	status &= TX_STATUS_MSK; | ||||
| @ -3071,6 +3101,10 @@ struct statistics_general { | ||||
| 	__le32 reserved3; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| #define UCODE_STATISTICS_CLEAR_MSK		(0x1 << 0) | ||||
| #define UCODE_STATISTICS_FREQUENCY_MSK		(0x1 << 1) | ||||
| #define UCODE_STATISTICS_NARROW_BAND_MSK	(0x1 << 2) | ||||
| 
 | ||||
| /*
 | ||||
|  * REPLY_STATISTICS_CMD = 0x9c, | ||||
|  * 3945 and 4965 identical. | ||||
|  | ||||
| @ -209,6 +209,7 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant) | ||||
| 	} | ||||
| 	return ant; | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_toggle_tx_ant); | ||||
| 
 | ||||
| const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||||
| EXPORT_SYMBOL(iwl_bcast_addr); | ||||
| @ -255,7 +256,10 @@ int iwl_hw_nic_init(struct iwl_priv *priv) | ||||
| 	/* nic_init */ | ||||
| 	spin_lock_irqsave(&priv->lock, flags); | ||||
| 	priv->cfg->ops->lib->apm_ops.init(priv); | ||||
| 	iwl_write32(priv, CSR_INT_COALESCING, 512 / 32); | ||||
| 
 | ||||
| 	/* Set interrupt coalescing timer to 512 usecs */ | ||||
| 	iwl_write8(priv, CSR_INT_COALESCING, 512 / 32); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&priv->lock, flags); | ||||
| 
 | ||||
| 	ret = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN); | ||||
| @ -446,13 +450,8 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, | ||||
| 	if (priv->cfg->ht_greenfield_support) | ||||
| 		ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; | ||||
| 	ht_info->cap |= IEEE80211_HT_CAP_SGI_20; | ||||
| 	if (priv->cfg->support_sm_ps) | ||||
| 		ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & | ||||
| 				     (WLAN_HT_CAP_SM_PS_DYNAMIC << 2)); | ||||
| 	else | ||||
| 		ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & | ||||
| 				     (WLAN_HT_CAP_SM_PS_DISABLED << 2)); | ||||
| 
 | ||||
| 	ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & | ||||
| 			     (priv->cfg->sm_ps_mode << 2)); | ||||
| 	max_bit_rate = MAX_BIT_RATE_20_MHZ; | ||||
| 	if (priv->hw_params.ht40_channel & BIT(band)) { | ||||
| 		ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||||
| @ -1007,25 +1006,23 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) | ||||
| 	int idle_cnt = active_cnt; | ||||
| 	bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); | ||||
| 
 | ||||
| 	if (priv->cfg->support_sm_ps) { | ||||
| 		/* # Rx chains when idling and maybe trying to save power */ | ||||
| 		switch (priv->current_ht_config.sm_ps) { | ||||
| 		case WLAN_HT_CAP_SM_PS_STATIC: | ||||
| 		case WLAN_HT_CAP_SM_PS_DYNAMIC: | ||||
| 			idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL : | ||||
| 				IWL_NUM_IDLE_CHAINS_SINGLE; | ||||
| 			break; | ||||
| 		case WLAN_HT_CAP_SM_PS_DISABLED: | ||||
| 			idle_cnt = (is_cam) ? active_cnt : | ||||
| 				IWL_NUM_IDLE_CHAINS_SINGLE; | ||||
| 			break; | ||||
| 		case WLAN_HT_CAP_SM_PS_INVALID: | ||||
| 		default: | ||||
| 			IWL_ERR(priv, "invalid sm_ps mode %d\n", | ||||
| 				priv->current_ht_config.sm_ps); | ||||
| 			WARN_ON(1); | ||||
| 			break; | ||||
| 		} | ||||
| 	/* # Rx chains when idling and maybe trying to save power */ | ||||
| 	switch (priv->cfg->sm_ps_mode) { | ||||
| 	case WLAN_HT_CAP_SM_PS_STATIC: | ||||
| 		idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE; | ||||
| 		break; | ||||
| 	case WLAN_HT_CAP_SM_PS_DYNAMIC: | ||||
| 		idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL : | ||||
| 			IWL_NUM_IDLE_CHAINS_SINGLE; | ||||
| 		break; | ||||
| 	case WLAN_HT_CAP_SM_PS_DISABLED: | ||||
| 		break; | ||||
| 	case WLAN_HT_CAP_SM_PS_INVALID: | ||||
| 	default: | ||||
| 		IWL_ERR(priv, "invalid sm_ps mode %u\n", | ||||
| 			priv->cfg->sm_ps_mode); | ||||
| 		WARN_ON(1); | ||||
| 		break; | ||||
| 	} | ||||
| 	return idle_cnt; | ||||
| } | ||||
| @ -1365,12 +1362,11 @@ void iwl_irq_handle_error(struct iwl_priv *priv) | ||||
| 	/* Cancel currently queued command. */ | ||||
| 	clear_bit(STATUS_HCMD_ACTIVE, &priv->status); | ||||
| 
 | ||||
| 	priv->cfg->ops->lib->dump_nic_error_log(priv); | ||||
| 	priv->cfg->ops->lib->dump_nic_event_log(priv, false); | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| 	if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) { | ||||
| 		priv->cfg->ops->lib->dump_nic_error_log(priv); | ||||
| 		priv->cfg->ops->lib->dump_nic_event_log(priv); | ||||
| 	if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) | ||||
| 		iwl_print_rx_config_cmd(priv); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	wake_up_interruptible(&priv->wait_command_queue); | ||||
| @ -1991,16 +1987,21 @@ int iwl_send_bt_config(struct iwl_priv *priv) | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_send_bt_config); | ||||
| 
 | ||||
| int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags) | ||||
| int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) | ||||
| { | ||||
| 	u32 stat_flags = 0; | ||||
| 	struct iwl_host_cmd cmd = { | ||||
| 		.id = REPLY_STATISTICS_CMD, | ||||
| 		.flags = flags, | ||||
| 		.len = sizeof(stat_flags), | ||||
| 		.data = (u8 *) &stat_flags, | ||||
| 	struct iwl_statistics_cmd statistics_cmd = { | ||||
| 		.configuration_flags = | ||||
| 			clear ? IWL_STATS_CONF_CLEAR_STATS : 0, | ||||
| 	}; | ||||
| 	return iwl_send_cmd(priv, &cmd); | ||||
| 
 | ||||
| 	if (flags & CMD_ASYNC) | ||||
| 		return iwl_send_cmd_pdu_async(priv, REPLY_STATISTICS_CMD, | ||||
| 					       sizeof(struct iwl_statistics_cmd), | ||||
| 					       &statistics_cmd, NULL); | ||||
| 	else | ||||
| 		return iwl_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, | ||||
| 					sizeof(struct iwl_statistics_cmd), | ||||
| 					&statistics_cmd); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_send_statistics_request); | ||||
| 
 | ||||
| @ -2477,6 +2478,16 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 		} else { | ||||
| 			priv->assoc_id = 0; | ||||
| 			iwl_led_disassociate(priv); | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * inform the ucode that there is no longer an | ||||
| 			 * association and that no more packets should be | ||||
| 			 * send | ||||
| 			 */ | ||||
| 			priv->staging_rxon.filter_flags &= | ||||
| 				~RXON_FILTER_ASSOC_MSK; | ||||
| 			priv->staging_rxon.assoc_id = 0; | ||||
| 			iwlcore_commit_rxon(priv); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -2492,6 +2503,14 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ((changes & BSS_CHANGED_BEACON_ENABLED) && | ||||
| 	    vif->bss_conf.enable_beacon) { | ||||
| 		memcpy(priv->staging_rxon.bssid_addr, | ||||
| 		       bss_conf->bssid, ETH_ALEN); | ||||
| 		memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 		iwlcore_config_ap(priv); | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	IWL_DEBUG_MAC80211(priv, "leave\n"); | ||||
| @ -3070,15 +3089,11 @@ const char *get_ctrl_string(int cmd) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void iwl_clear_tx_stats(struct iwl_priv *priv) | ||||
| void iwl_clear_traffic_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)); | ||||
| 	priv->led_tpt = 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -3171,6 +3186,7 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) | ||||
| 		stats->data_cnt++; | ||||
| 		stats->data_bytes += len; | ||||
| 	} | ||||
| 	iwl_leds_background(priv); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_update_stats); | ||||
| #endif | ||||
|  | ||||
| @ -167,7 +167,7 @@ struct iwl_lib_ops { | ||||
| 	int (*is_valid_rtc_data_addr)(u32 addr); | ||||
| 	/* 1st ucode load */ | ||||
| 	int (*load_ucode)(struct iwl_priv *priv); | ||||
| 	void (*dump_nic_event_log)(struct iwl_priv *priv); | ||||
| 	void (*dump_nic_event_log)(struct iwl_priv *priv, bool full_log); | ||||
| 	void (*dump_nic_error_log)(struct iwl_priv *priv); | ||||
| 	int (*set_channel_switch)(struct iwl_priv *priv, u16 channel); | ||||
| 	/* power management */ | ||||
| @ -228,7 +228,7 @@ struct iwl_mod_params { | ||||
|  * @chain_noise_num_beacons: number of beacons used to compute chain noise | ||||
|  * @adv_thermal_throttle: support advance thermal throttle | ||||
|  * @support_ct_kill_exit: support ct kill exit condition | ||||
|  * @support_sm_ps: support spatial multiplexing power save | ||||
|  * @sm_ps_mode: spatial multiplexing power save mode | ||||
|  * @support_wimax_coexist: support wimax/wifi co-exist | ||||
|  * | ||||
|  * We enable the driver to be backward compatible wrt API version. The | ||||
| @ -285,7 +285,7 @@ struct iwl_cfg { | ||||
| 	const bool supports_idle; | ||||
| 	bool adv_thermal_throttle; | ||||
| 	bool support_ct_kill_exit; | ||||
| 	bool support_sm_ps; | ||||
| 	u8 sm_ps_mode; | ||||
| 	const bool support_wimax_coexist; | ||||
| }; | ||||
| 
 | ||||
| @ -353,8 +353,7 @@ 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_clear_traffic_stats(struct iwl_priv *priv); | ||||
| void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, | ||||
| 		      u16 len); | ||||
| #else | ||||
| @ -390,6 +389,7 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx, | ||||
| 		/* data */ | ||||
| 		stats->data_bytes += len; | ||||
| 	} | ||||
| 	iwl_leds_background(priv); | ||||
| } | ||||
| #endif | ||||
| /*****************************************************
 | ||||
| @ -425,6 +425,8 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, | ||||
| 			       struct iwl_rx_mem_buffer *rxb); | ||||
| void iwl_rx_statistics(struct iwl_priv *priv, | ||||
| 			      struct iwl_rx_mem_buffer *rxb); | ||||
| void iwl_reply_statistics(struct iwl_priv *priv, | ||||
| 			  struct iwl_rx_mem_buffer *rxb); | ||||
| void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); | ||||
| 
 | ||||
| /* TX helpers */ | ||||
| @ -576,19 +578,11 @@ int iwl_pci_resume(struct pci_dev *pdev); | ||||
| /*****************************************************
 | ||||
| *  Error Handling Debugging | ||||
| ******************************************************/ | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| void iwl_dump_nic_event_log(struct iwl_priv *priv); | ||||
| void iwl_dump_nic_error_log(struct iwl_priv *priv); | ||||
| void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log); | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| void iwl_print_rx_config_cmd(struct iwl_priv *priv); | ||||
| #else | ||||
| static inline void iwl_dump_nic_event_log(struct iwl_priv *priv) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void iwl_dump_nic_error_log(struct iwl_priv *priv) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv) | ||||
| { | ||||
| } | ||||
| @ -669,7 +663,8 @@ static inline int iwl_is_ready_rf(struct iwl_priv *priv) | ||||
| 
 | ||||
| extern void iwl_rf_kill_ct_config(struct iwl_priv *priv); | ||||
| extern int iwl_send_bt_config(struct iwl_priv *priv); | ||||
| extern int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags); | ||||
| extern int iwl_send_statistics_request(struct iwl_priv *priv, | ||||
| 				       u8 flags, bool clear); | ||||
| extern int iwl_verify_ucode(struct iwl_priv *priv); | ||||
| extern int iwl_send_lq_cmd(struct iwl_priv *priv, | ||||
| 		struct iwl_link_quality_cmd *lq, u8 flags); | ||||
|  | ||||
| @ -62,11 +62,29 @@ | ||||
|  *****************************************************************************/ | ||||
| #ifndef __iwl_csr_h__ | ||||
| #define __iwl_csr_h__ | ||||
| /*=== CSR (control and status registers) ===*/ | ||||
| /*
 | ||||
|  * CSR (control and status registers) | ||||
|  * | ||||
|  * CSR registers are mapped directly into PCI bus space, and are accessible | ||||
|  * whenever platform supplies power to device, even when device is in | ||||
|  * low power states due to driver-invoked device resets | ||||
|  * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. | ||||
|  * | ||||
|  * Use iwl_write32() and iwl_read32() family to access these registers; | ||||
|  * these provide simple PCI bus access, without waking up the MAC. | ||||
|  * Do not use iwl_write_direct32() family for these registers; | ||||
|  * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. | ||||
|  * The MAC (uCode processor, etc.) does not need to be powered up for accessing | ||||
|  * the CSR registers. | ||||
|  * | ||||
|  * NOTE:  Newer devices using one-time-programmable (OTP) memory | ||||
|  *        require device to be awake in order to read this memory | ||||
|  *        via CSR_EEPROM and CSR_OTP registers | ||||
|  */ | ||||
| #define CSR_BASE    (0x000) | ||||
| 
 | ||||
| #define CSR_HW_IF_CONFIG_REG    (CSR_BASE+0x000) /* hardware interface config */ | ||||
| #define CSR_INT_COALESCING     (CSR_BASE+0x004) /* accum ints, 32-usec units */ | ||||
| #define CSR_INT_COALESCING      (CSR_BASE+0x004) /* accum ints, 32-usec units */ | ||||
| #define CSR_INT                 (CSR_BASE+0x008) /* host interrupt status/ack */ | ||||
| #define CSR_INT_MASK            (CSR_BASE+0x00c) /* host interrupt enable */ | ||||
| #define CSR_FH_INT_STATUS       (CSR_BASE+0x010) /* busmaster int status/ack*/ | ||||
| @ -74,42 +92,65 @@ | ||||
| #define CSR_RESET               (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/ | ||||
| #define CSR_GP_CNTRL            (CSR_BASE+0x024) | ||||
| 
 | ||||
| /* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */ | ||||
| #define CSR_INT_PERIODIC_REG	(CSR_BASE+0x005) | ||||
| 
 | ||||
| /*
 | ||||
|  * Hardware revision info | ||||
|  * Bit fields: | ||||
|  * 31-8:  Reserved | ||||
|  *  7-4:  Type of device:  0x0 = 4965, 0xd = 3945 | ||||
|  *  7-4:  Type of device:  see CSR_HW_REV_TYPE_xxx definitions | ||||
|  *  3-2:  Revision step:  0 = A, 1 = B, 2 = C, 3 = D | ||||
|  *  1-0:  "Dash" value, as in A-1, etc. | ||||
|  *  1-0:  "Dash" (-) value, as in A-1, etc. | ||||
|  * | ||||
|  * NOTE:  Revision step affects calculation of CCK txpower for 4965. | ||||
|  * NOTE:  See also CSR_HW_REV_WA_REG (work-around for bug in 4965). | ||||
|  */ | ||||
| #define CSR_HW_REV              (CSR_BASE+0x028) | ||||
| 
 | ||||
| /* EEPROM reads */ | ||||
| /*
 | ||||
|  * EEPROM and OTP (one-time-programmable) memory reads | ||||
|  * | ||||
|  * NOTE:  For (newer) devices using OTP, device must be awake, initialized via | ||||
|  *        apm_ops.init() in order to read.  Older devices (3945/4965/5000) | ||||
|  *        use EEPROM and do not require this. | ||||
|  */ | ||||
| #define CSR_EEPROM_REG          (CSR_BASE+0x02c) | ||||
| #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_REG	(CSR_BASE+0x048) | ||||
| #define CSR_GP_DRIVER_REG	(CSR_BASE+0x050) | ||||
| 
 | ||||
| /*
 | ||||
|  * UCODE-DRIVER GP (general purpose) mailbox registers. | ||||
|  * SET/CLR registers set/clear bit(s) if "1" is written. | ||||
|  */ | ||||
| #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) | ||||
| #define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060) | ||||
| 
 | ||||
| #define CSR_LED_REG             (CSR_BASE+0x094) | ||||
| #define CSR_DRAM_INT_TBL_REG	(CSR_BASE+0x0A0) | ||||
| 
 | ||||
| /* GIO Chicken Bits (PCI Express bus link power management) */ | ||||
| #define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100) | ||||
| 
 | ||||
| #define CSR_INT_PERIODIC_REG	(CSR_BASE+0x005) | ||||
| /* Analog phase-lock-loop configuration  */ | ||||
| #define CSR_ANA_PLL_CFG         (CSR_BASE+0x20c) | ||||
| 
 | ||||
| /*
 | ||||
|  * Indicates hardware rev, to determine CCK backoff for txpower calculation. | ||||
|  * CSR Hardware Revision Workaround Register.  Indicates hardware rev; | ||||
|  * "step" determines CCK backoff for txpower calculation.  Used for 4965 only. | ||||
|  * See also CSR_HW_REV register. | ||||
|  * Bit fields: | ||||
|  *  3-2:  0 = A, 1 = B, 2 = C, 3 = D step | ||||
|  *  1-0:  "Dash" (-) value, as in C-1, etc. | ||||
|  */ | ||||
| #define CSR_HW_REV_WA_REG		(CSR_BASE+0x22C) | ||||
| 
 | ||||
| #define CSR_DBG_HPET_MEM_REG		(CSR_BASE+0x240) | ||||
| #define CSR_DBG_LINK_PWR_MGMT_REG	(CSR_BASE+0x250) | ||||
| 
 | ||||
| @ -126,14 +167,14 @@ | ||||
| #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A    (0x00000000) | ||||
| #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B    (0x00001000) | ||||
| 
 | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A		(0x00080000) | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM		(0x00200000) | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY		(0x00400000) | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE	(0x02000000) | ||||
| #define CSR_HW_IF_CONFIG_REG_PREPARE			(0x08000000) | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A	(0x00080000) | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM	(0x00200000) | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY	(0x00400000) /* PCI_OWN_SEM */ | ||||
| #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ | ||||
| #define CSR_HW_IF_CONFIG_REG_PREPARE		  (0x08000000) /* WAKE_ME */ | ||||
| 
 | ||||
| #define CSR_INT_PERIODIC_DIS			(0x00) | ||||
| #define CSR_INT_PERIODIC_ENA			(0xFF) | ||||
| #define CSR_INT_PERIODIC_DIS			(0x00) /* disable periodic int*/ | ||||
| #define CSR_INT_PERIODIC_ENA			(0xFF) /* 255*32 usec ~ 8 msec*/ | ||||
| 
 | ||||
| /* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
 | ||||
|  * acknowledged (reset) by host writing "1" to flagged bits. */ | ||||
| @ -198,7 +239,44 @@ | ||||
| #define CSR_RESET_REG_FLAG_STOP_MASTER               (0x00000200) | ||||
| #define CSR_RESET_LINK_PWR_MGMT_DISABLED             (0x80000000) | ||||
| 
 | ||||
| /* GP (general purpose) CONTROL */ | ||||
| /*
 | ||||
|  * GP (general purpose) CONTROL REGISTER | ||||
|  * Bit fields: | ||||
|  *    27:  HW_RF_KILL_SW | ||||
|  *         Indicates state of (platform's) hardware RF-Kill switch | ||||
|  * 26-24:  POWER_SAVE_TYPE | ||||
|  *         Indicates current power-saving mode: | ||||
|  *         000 -- No power saving | ||||
|  *         001 -- MAC power-down | ||||
|  *         010 -- PHY (radio) power-down | ||||
|  *         011 -- Error | ||||
|  *   9-6:  SYS_CONFIG | ||||
|  *         Indicates current system configuration, reflecting pins on chip | ||||
|  *         as forced high/low by device circuit board. | ||||
|  *     4:  GOING_TO_SLEEP | ||||
|  *         Indicates MAC is entering a power-saving sleep power-down. | ||||
|  *         Not a good time to access device-internal resources. | ||||
|  *     3:  MAC_ACCESS_REQ | ||||
|  *         Host sets this to request and maintain MAC wakeup, to allow host | ||||
|  *         access to device-internal resources.  Host must wait for | ||||
|  *         MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR | ||||
|  *         device registers. | ||||
|  *     2:  INIT_DONE | ||||
|  *         Host sets this to put device into fully operational D0 power mode. | ||||
|  *         Host resets this after SW_RESET to put device into low power mode. | ||||
|  *     0:  MAC_CLOCK_READY | ||||
|  *         Indicates MAC (ucode processor, etc.) is powered up and can run. | ||||
|  *         Internal resources are accessible. | ||||
|  *         NOTE:  This does not indicate that the processor is actually running. | ||||
|  *         NOTE:  This does not indicate that 4965 or 3945 has completed | ||||
|  *                init or post-power-down restore of internal SRAM memory. | ||||
|  *                Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that | ||||
|  *                SRAM is restored and uCode is in normal operation mode. | ||||
|  *                Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and | ||||
|  *                do not need to save/restore it. | ||||
|  *         NOTE:  After device reset, this bit remains "0" until host sets | ||||
|  *                INIT_DONE | ||||
|  */ | ||||
| #define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY        (0x00000001) | ||||
| #define CSR_GP_CNTRL_REG_FLAG_INIT_DONE              (0x00000004) | ||||
| #define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ         (0x00000008) | ||||
| @ -231,28 +309,58 @@ | ||||
| #define CSR_EEPROM_REG_MSK_DATA		(0xFFFF0000) | ||||
| 
 | ||||
| /* EEPROM GP */ | ||||
| #define CSR_EEPROM_GP_VALID_MSK		(0x00000007) | ||||
| #define CSR_EEPROM_GP_VALID_MSK		(0x00000007) /* signature */ | ||||
| #define CSR_EEPROM_GP_IF_OWNER_MSK	(0x00000180) | ||||
| #define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP	(0x00000000) | ||||
| #define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP		(0x00000001) | ||||
| #define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K		(0x00000002) | ||||
| #define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K		(0x00000004) | ||||
| 
 | ||||
| /* One-time-programmable memory general purpose reg */ | ||||
| #define CSR_OTP_GP_REG_DEVICE_SELECT	(0x00010000) /* 0 - EEPROM, 1 - OTP */ | ||||
| #define CSR_OTP_GP_REG_OTP_ACCESS_MODE	(0x00020000) /* 0 - absolute, 1 - relative */ | ||||
| #define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK          (0x00100000) /* bit 20 */ | ||||
| #define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK        (0x00200000) /* bit 21 */ | ||||
| 
 | ||||
| /* GP REG */ | ||||
| #define CSR_GP_REG_POWER_SAVE_STATUS_MSK            (0x03000000) /* bit 24/25 */ | ||||
| #define CSR_GP_REG_NO_POWER_SAVE            (0x00000000) | ||||
| #define CSR_GP_REG_MAC_POWER_SAVE           (0x01000000) | ||||
| #define CSR_GP_REG_PHY_POWER_SAVE           (0x02000000) | ||||
| #define CSR_GP_REG_POWER_SAVE_ERROR         (0x03000000) | ||||
| 
 | ||||
| /* EEPROM signature */ | ||||
| #define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP	(0x00000000) | ||||
| #define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP		(0x00000001) | ||||
| #define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K		(0x00000002) | ||||
| #define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K		(0x00000004) | ||||
| 
 | ||||
| /* CSR GIO */ | ||||
| #define CSR_GIO_REG_VAL_L0S_ENABLED	(0x00000002) | ||||
| 
 | ||||
| /* UCODE DRV GP */ | ||||
| /*
 | ||||
|  * UCODE-DRIVER GP (general purpose) mailbox register 1 | ||||
|  * Host driver and uCode write and/or read this register to communicate with | ||||
|  * each other. | ||||
|  * Bit fields: | ||||
|  *     4:  UCODE_DISABLE | ||||
|  *         Host sets this to request permanent halt of uCode, same as | ||||
|  *         sending CARD_STATE command with "halt" bit set. | ||||
|  *     3:  CT_KILL_EXIT | ||||
|  *         Host sets this to request exit from CT_KILL state, i.e. host thinks | ||||
|  *         device temperature is low enough to continue normal operation. | ||||
|  *     2:  CMD_BLOCKED | ||||
|  *         Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) | ||||
|  *         to release uCode to clear all Tx and command queues, enter | ||||
|  *         unassociated mode, and power down. | ||||
|  *         NOTE:  Some devices also use HBUS_TARG_MBX_C register for this bit. | ||||
|  *     1:  SW_BIT_RFKILL | ||||
|  *         Host sets this when issuing CARD_STATE command to request | ||||
|  *         device sleep. | ||||
|  *     0:  MAC_SLEEP | ||||
|  *         uCode sets this when preparing a power-saving power-down. | ||||
|  *         uCode resets this when power-up is complete and SRAM is sane. | ||||
|  *         NOTE:  3945/4965 saves internal SRAM data to host when powering down, | ||||
|  *                and must restore this data after powering back up. | ||||
|  *                MAC_SLEEP is the best indication that restore is complete. | ||||
|  *                Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and | ||||
|  *                do not need to save/restore it. | ||||
|  */ | ||||
| #define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP             (0x00000001) | ||||
| #define CSR_UCODE_SW_BIT_RFKILL                     (0x00000002) | ||||
| #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED           (0x00000004) | ||||
| @ -265,7 +373,7 @@ | ||||
| #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA	    (0x00000002) | ||||
| 
 | ||||
| 
 | ||||
| /* GI Chicken Bits */ | ||||
| /* GIO Chicken Bits (PCI Express bus link power management) */ | ||||
| #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX  (0x00800000) | ||||
| #define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER  (0x20000000) | ||||
| 
 | ||||
| @ -285,8 +393,23 @@ | ||||
| #define CSR_DRAM_INT_TBL_ENABLE		(1 << 31) | ||||
| #define CSR_DRAM_INIT_TBL_WRAP_CHECK	(1 << 27) | ||||
| 
 | ||||
| /*=== HBUS (Host-side Bus) ===*/ | ||||
| /*
 | ||||
|  * HBUS (Host-side Bus) | ||||
|  * | ||||
|  * HBUS registers are mapped directly into PCI bus space, but are used | ||||
|  * to indirectly access device's internal memory or registers that | ||||
|  * may be powered-down. | ||||
|  * | ||||
|  * Use iwl_write_direct32()/iwl_read_direct32() family for these registers; | ||||
|  * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ | ||||
|  * to make sure the MAC (uCode processor, etc.) is powered up for accessing | ||||
|  * internal resources. | ||||
|  * | ||||
|  * Do not use iwl_write32()/iwl_read32() family to access these registers; | ||||
|  * these provide only simple PCI bus access, without waking up the MAC. | ||||
|  */ | ||||
| #define HBUS_BASE	(0x400) | ||||
| 
 | ||||
| /*
 | ||||
|  * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM | ||||
|  * structures, error log, event log, verifying uCode load). | ||||
| @ -301,6 +424,10 @@ | ||||
| #define HBUS_TARG_MEM_WDAT      (HBUS_BASE+0x018) | ||||
| #define HBUS_TARG_MEM_RDAT      (HBUS_BASE+0x01c) | ||||
| 
 | ||||
| /* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ | ||||
| #define HBUS_TARG_MBX_C         (HBUS_BASE+0x030) | ||||
| #define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED         (0x00000004) | ||||
| 
 | ||||
| /*
 | ||||
|  * Registers for accessing device's internal peripheral registers | ||||
|  * (e.g. SCD, BSM, etc.).  First write to address register, | ||||
| @ -315,16 +442,12 @@ | ||||
| #define HBUS_TARG_PRPH_RDAT     (HBUS_BASE+0x050) | ||||
| 
 | ||||
| /*
 | ||||
|  * Per-Tx-queue write pointer (index, really!) (3945 and 4965). | ||||
|  * Per-Tx-queue write pointer (index, really!) | ||||
|  * Indicates index to next TFD that driver will fill (1 past latest filled). | ||||
|  * Bit usage: | ||||
|  *  0-7:  queue write index | ||||
|  * 11-8:  queue selector | ||||
|  */ | ||||
| #define HBUS_TARG_WRPTR         (HBUS_BASE+0x060) | ||||
| #define HBUS_TARG_MBX_C         (HBUS_BASE+0x030) | ||||
| 
 | ||||
| #define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED         (0x00000004) | ||||
| 
 | ||||
| 
 | ||||
| #endif /* !__iwl_csr_h__ */ | ||||
|  | ||||
| @ -107,6 +107,8 @@ struct iwl_debugfs { | ||||
| 		struct dentry *file_chain_noise; | ||||
| 		struct dentry *file_tx_power; | ||||
| 		struct dentry *file_power_save_status; | ||||
| 		struct dentry *file_clear_ucode_statistics; | ||||
| 		struct dentry *file_clear_traffic_statistics; | ||||
| 	} dbgfs_debug_files; | ||||
| 	u32 sram_offset; | ||||
| 	u32 sram_len; | ||||
|  | ||||
| @ -47,9 +47,9 @@ | ||||
| 		goto err; 						\ | ||||
| } while (0) | ||||
| 
 | ||||
| #define DEBUGFS_ADD_FILE(name, parent) do {                             \ | ||||
| #define DEBUGFS_ADD_FILE(name, parent, mode) do {                       \ | ||||
| 	dbgfs->dbgfs_##parent##_files.file_##name =                     \ | ||||
| 	debugfs_create_file(#name, S_IWUSR | S_IRUSR,                   \ | ||||
| 	debugfs_create_file(#name, mode,                                \ | ||||
| 				dbgfs->dir_##parent, priv,              \ | ||||
| 				&iwl_dbgfs_##name##_ops);               \ | ||||
| 	if (!(dbgfs->dbgfs_##parent##_files.file_##name))               \ | ||||
| @ -131,21 +131,22 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, | ||||
| 
 | ||||
| 	int cnt; | ||||
| 	ssize_t ret; | ||||
| 	const size_t bufsz = 100 + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); | ||||
| 	const size_t bufsz = 100 + | ||||
| 		sizeof(char) * 50 * (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", | ||||
| 				 "\t%25s\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", | ||||
| 				 "\t%25s\t\t: %u\n", | ||||
| 				 get_ctrl_string(cnt), | ||||
| 				 priv->tx_stats.ctrl[cnt]); | ||||
| 	} | ||||
| @ -159,7 +160,7 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file, | ||||
| static ssize_t iwl_dbgfs_clear_traffic_statistics_write(struct file *file, | ||||
| 					const char __user *user_buf, | ||||
| 					size_t count, loff_t *ppos) | ||||
| { | ||||
| @ -174,8 +175,7 @@ static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file, | ||||
| 		return -EFAULT; | ||||
| 	if (sscanf(buf, "%x", &clear_flag) != 1) | ||||
| 		return -EFAULT; | ||||
| 	if (clear_flag == 1) | ||||
| 		iwl_clear_tx_stats(priv); | ||||
| 	iwl_clear_traffic_stats(priv); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| @ -190,7 +190,7 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, | ||||
| 	int cnt; | ||||
| 	ssize_t ret; | ||||
| 	const size_t bufsz = 100 + | ||||
| 		sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); | ||||
| 		sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); | ||||
| 	buf = kzalloc(bufsz, GFP_KERNEL); | ||||
| 	if (!buf) | ||||
| 		return -ENOMEM; | ||||
| @ -198,14 +198,14 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, | ||||
| 	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", | ||||
| 				 "\t%25s\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", | ||||
| 				 "\t%25s\t\t: %u\n", | ||||
| 				 get_ctrl_string(cnt), | ||||
| 				 priv->rx_stats.ctrl[cnt]); | ||||
| 	} | ||||
| @ -220,26 +220,6 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, | ||||
| 	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; | ||||
| #define BYTE2_MASK 0x0000ffff; | ||||
| #define BYTE3_MASK 0x00ffffff; | ||||
| @ -248,13 +228,29 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, | ||||
| 					size_t count, loff_t *ppos) | ||||
| { | ||||
| 	u32 val; | ||||
| 	char buf[1024]; | ||||
| 	char *buf; | ||||
| 	ssize_t ret; | ||||
| 	int i; | ||||
| 	int pos = 0; | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)file->private_data; | ||||
| 	const size_t bufsz = sizeof(buf); | ||||
| 	size_t bufsz; | ||||
| 
 | ||||
| 	/* default is to dump the entire data segment */ | ||||
| 	if (!priv->dbgfs->sram_offset && !priv->dbgfs->sram_len) { | ||||
| 		priv->dbgfs->sram_offset = 0x800000; | ||||
| 		if (priv->ucode_type == UCODE_INIT) | ||||
| 			priv->dbgfs->sram_len = priv->ucode_init_data.len; | ||||
| 		else | ||||
| 			priv->dbgfs->sram_len = priv->ucode_data.len; | ||||
| 	} | ||||
| 	bufsz =  30 + priv->dbgfs->sram_len * sizeof(char) * 10; | ||||
| 	buf = kmalloc(bufsz, GFP_KERNEL); | ||||
| 	if (!buf) | ||||
| 		return -ENOMEM; | ||||
| 	pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", | ||||
| 			priv->dbgfs->sram_len); | ||||
| 	pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", | ||||
| 			priv->dbgfs->sram_offset); | ||||
| 	for (i = priv->dbgfs->sram_len; i > 0; i -= 4) { | ||||
| 		val = iwl_read_targ_mem(priv, priv->dbgfs->sram_offset + \ | ||||
| 					priv->dbgfs->sram_len - i); | ||||
| @ -271,11 +267,14 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (!(i % 16)) | ||||
| 			pos += scnprintf(buf + pos, bufsz - pos, "\n"); | ||||
| 		pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val); | ||||
| 	} | ||||
| 	pos += scnprintf(buf + pos, bufsz - pos, "\n"); | ||||
| 
 | ||||
| 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); | ||||
| 	kfree(buf); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| @ -335,8 +334,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, | ||||
| 			pos += scnprintf(buf + pos, bufsz - pos, | ||||
| 					"flags: 0x%x\n", | ||||
| 					station->sta.station_flags_msk); | ||||
| 			pos += scnprintf(buf + pos, bufsz - pos, | ||||
| 					"ps_status: %u\n", station->ps_status); | ||||
| 			pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n"); | ||||
| 			pos += scnprintf(buf + pos, bufsz - pos, | ||||
| 					"seq_num\t\ttxq_id"); | ||||
| @ -439,7 +436,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, | ||||
| 	if (sscanf(buf, "%d", &event_log_flag) != 1) | ||||
| 		return -EFAULT; | ||||
| 	if (event_log_flag == 1) | ||||
| 		priv->cfg->ops->lib->dump_nic_event_log(priv); | ||||
| 		priv->cfg->ops->lib->dump_nic_event_log(priv, true); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| @ -986,7 +983,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, | ||||
| 	int pos = 0; | ||||
| 	int cnt; | ||||
| 	int ret; | ||||
| 	const size_t bufsz = sizeof(char) * 60 * priv->cfg->num_of_queues; | ||||
| 	const size_t bufsz = sizeof(char) * 64 * priv->cfg->num_of_queues; | ||||
| 
 | ||||
| 	if (!priv->txq) { | ||||
| 		IWL_ERR(priv, "txq not ready\n"); | ||||
| @ -1042,10 +1039,6 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, | ||||
| 	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) | ||||
| { | ||||
| @ -1092,7 +1085,7 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, | ||||
| 
 | ||||
| 	/* make request to uCode to retrieve statistics information */ | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	ret = iwl_send_statistics_request(priv, 0); | ||||
| 	ret = iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| @ -1398,7 +1391,7 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, | ||||
| 
 | ||||
| 	/* make request to uCode to retrieve statistics information */ | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	ret = iwl_send_statistics_request(priv, 0); | ||||
| 	ret = iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| @ -1542,7 +1535,7 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, | ||||
| 
 | ||||
| 	/* make request to uCode to retrieve statistics information */ | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	ret = iwl_send_statistics_request(priv, 0); | ||||
| 	ret = iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| @ -1770,7 +1763,7 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file, | ||||
| 	else { | ||||
| 		/* make request to uCode to retrieve statistics information */ | ||||
| 		mutex_lock(&priv->mutex); | ||||
| 		ret = iwl_send_statistics_request(priv, 0); | ||||
| 		ret = iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 		mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 		if (ret) { | ||||
| @ -1828,8 +1821,32 @@ static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, | ||||
| 	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); | ||||
| static ssize_t iwl_dbgfs_clear_ucode_statistics_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 clear; | ||||
| 
 | ||||
| 	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", &clear) != 1) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	/* make request to uCode to retrieve statistics information */ | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	iwl_send_statistics_request(priv, CMD_SYNC, true); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| DEBUGFS_READ_FILE_OPS(rx_statistics); | ||||
| DEBUGFS_READ_FILE_OPS(tx_statistics); | ||||
| DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); | ||||
| DEBUGFS_READ_FILE_OPS(rx_queue); | ||||
| DEBUGFS_READ_FILE_OPS(tx_queue); | ||||
| @ -1840,6 +1857,8 @@ DEBUGFS_READ_FILE_OPS(sensitivity); | ||||
| DEBUGFS_READ_FILE_OPS(chain_noise); | ||||
| DEBUGFS_READ_FILE_OPS(tx_power); | ||||
| DEBUGFS_READ_FILE_OPS(power_save_status); | ||||
| DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); | ||||
| DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics); | ||||
| 
 | ||||
| /*
 | ||||
|  * Create the debugfs files and directories | ||||
| @ -1868,32 +1887,34 @@ 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(channels, data); | ||||
| 	DEBUGFS_ADD_FILE(status, data); | ||||
| 	DEBUGFS_ADD_FILE(interrupt, data); | ||||
| 	DEBUGFS_ADD_FILE(qos, data); | ||||
| 	DEBUGFS_ADD_FILE(led, data); | ||||
| 	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); | ||||
| 	DEBUGFS_ADD_FILE(power_save_status, debug); | ||||
| 	DEBUGFS_ADD_FILE(nvm, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(sram, data, S_IWUSR | S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(log_event, data, S_IWUSR); | ||||
| 	DEBUGFS_ADD_FILE(stations, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(channels, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(status, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(interrupt, data, S_IWUSR | S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(qos, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(led, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(sleep_level_override, data, S_IWUSR | S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(current_sleep_command, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(thermal_throttling, data, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(disable_ht40, data, S_IWUSR | S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(rx_statistics, debug, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(tx_statistics, debug, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(traffic_log, debug, S_IWUSR | S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(rx_queue, debug, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(tx_queue, debug, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(tx_power, debug, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(power_save_status, debug, S_IRUSR); | ||||
| 	DEBUGFS_ADD_FILE(clear_ucode_statistics, debug, S_IWUSR); | ||||
| 	DEBUGFS_ADD_FILE(clear_traffic_statistics, debug, S_IWUSR); | ||||
| 	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_FILE(ucode_rx_stats, debug, S_IRUSR); | ||||
| 		DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR); | ||||
| 		DEBUGFS_ADD_FILE(ucode_general_stats, debug, S_IRUSR); | ||||
| 		DEBUGFS_ADD_FILE(sensitivity, debug, S_IRUSR); | ||||
| 		DEBUGFS_ADD_FILE(chain_noise, debug, S_IRUSR); | ||||
| 	} | ||||
| 	DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal); | ||||
| 	DEBUGFS_ADD_BOOL(disable_chain_noise, rf, | ||||
| @ -1941,6 +1962,10 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) | ||||
| 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue); | ||||
| 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power); | ||||
| 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_power_save_status); | ||||
| 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. | ||||
| 			file_clear_ucode_statistics); | ||||
| 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. | ||||
| 			file_clear_traffic_statistics); | ||||
| 	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); | ||||
|  | ||||
| @ -52,19 +52,16 @@ extern struct iwl_cfg iwl4965_agn_cfg; | ||||
| extern struct iwl_cfg iwl5300_agn_cfg; | ||||
| extern struct iwl_cfg iwl5100_agn_cfg; | ||||
| extern struct iwl_cfg iwl5350_agn_cfg; | ||||
| extern struct iwl_cfg iwl5100_bg_cfg; | ||||
| extern struct iwl_cfg iwl5100_bgn_cfg; | ||||
| extern struct iwl_cfg iwl5100_abg_cfg; | ||||
| extern struct iwl_cfg iwl5150_agn_cfg; | ||||
| extern struct iwl_cfg iwl6000h_2agn_cfg; | ||||
| extern struct iwl_cfg iwl6000h_2abg_cfg; | ||||
| extern struct iwl_cfg iwl6000h_2bg_cfg; | ||||
| extern struct iwl_cfg iwl5150_abg_cfg; | ||||
| extern struct iwl_cfg iwl6000i_2agn_cfg; | ||||
| extern struct iwl_cfg iwl6000i_2abg_cfg; | ||||
| extern struct iwl_cfg iwl6000i_2bg_cfg; | ||||
| extern struct iwl_cfg iwl6000_3agn_cfg; | ||||
| extern struct iwl_cfg iwl6050_2agn_cfg; | ||||
| extern struct iwl_cfg iwl6050_2abg_cfg; | ||||
| extern struct iwl_cfg iwl6050_3agn_cfg; | ||||
| extern struct iwl_cfg iwl1000_bgn_cfg; | ||||
| extern struct iwl_cfg iwl1000_bg_cfg; | ||||
| 
 | ||||
| @ -295,9 +292,6 @@ struct iwl_channel_info { | ||||
| 
 | ||||
| 	/* 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_* */ | ||||
| 
 | ||||
| @ -518,7 +512,6 @@ struct iwl_ht_config { | ||||
| 	bool is_ht; | ||||
| 	bool is_40mhz; | ||||
| 	bool single_chain_sufficient; | ||||
| 	u8 sm_ps; | ||||
| 	/* BSS related data */ | ||||
| 	u8 extension_chan_offset; | ||||
| 	u8 ht_protection; | ||||
| @ -552,23 +545,10 @@ struct iwl_qos_info { | ||||
| 	struct iwl_qosparam_cmd def_qos_parm; | ||||
| }; | ||||
| 
 | ||||
| #define STA_PS_STATUS_WAKE             0 | ||||
| #define STA_PS_STATUS_SLEEP            1 | ||||
| 
 | ||||
| 
 | ||||
| struct iwl3945_station_entry { | ||||
| 	struct iwl3945_addsta_cmd sta; | ||||
| 	struct iwl_tid_data tid[MAX_TID_COUNT]; | ||||
| 	u8 used; | ||||
| 	u8 ps_status; | ||||
| 	struct iwl_hw_key keyinfo; | ||||
| }; | ||||
| 
 | ||||
| struct iwl_station_entry { | ||||
| 	struct iwl_addsta_cmd sta; | ||||
| 	struct iwl_tid_data tid[MAX_TID_COUNT]; | ||||
| 	u8 used; | ||||
| 	u8 ps_status; | ||||
| 	struct iwl_hw_key keyinfo; | ||||
| }; | ||||
| 
 | ||||
| @ -578,11 +558,12 @@ struct iwl_station_entry { | ||||
|  * When mac80211 creates a station it reserves some space (hw->sta_data_size) | ||||
|  * in the structure for use by driver. This structure is places in that | ||||
|  * space. | ||||
|  * | ||||
|  * At the moment use it for the station's rate scaling information. | ||||
|  */ | ||||
| struct iwl_station_priv { | ||||
| 	struct iwl_lq_sta lq_sta; | ||||
| 	atomic_t pending_frames; | ||||
| 	bool client; | ||||
| 	bool asleep; | ||||
| }; | ||||
| 
 | ||||
| /* one for each uCode image (inst/data, boot/init/runtime) */ | ||||
| @ -1254,6 +1235,7 @@ struct iwl_priv { | ||||
| 	/* TX Power */ | ||||
| 	s8 tx_power_user_lmt; | ||||
| 	s8 tx_power_device_lmt; | ||||
| 	s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */ | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #define CREATE_TRACE_POINTS | ||||
| #include "iwl-devtrace.h" | ||||
| 
 | ||||
| EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8); | ||||
| EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32); | ||||
| EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); | ||||
| EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx); | ||||
|  | ||||
| @ -14,7 +14,7 @@ static inline void trace_ ## name(proto) {} | ||||
| #define PRIV_ASSIGN	__entry->priv = priv | ||||
| 
 | ||||
| #undef TRACE_SYSTEM | ||||
| #define TRACE_SYSTEM iwlwifi | ||||
| #define TRACE_SYSTEM iwlwifi_io | ||||
| 
 | ||||
| TRACE_EVENT(iwlwifi_dev_ioread32, | ||||
| 	TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), | ||||
| @ -32,6 +32,22 @@ TRACE_EVENT(iwlwifi_dev_ioread32, | ||||
| 	TP_printk("[%p] read io[%#x] = %#x", __entry->priv, __entry->offs, __entry->val) | ||||
| ); | ||||
| 
 | ||||
| TRACE_EVENT(iwlwifi_dev_iowrite8, | ||||
| 	TP_PROTO(struct iwl_priv *priv, u32 offs, u8 val), | ||||
| 	TP_ARGS(priv, offs, val), | ||||
| 	TP_STRUCT__entry( | ||||
| 		PRIV_ENTRY | ||||
| 		__field(u32, offs) | ||||
| 		__field(u8, val) | ||||
| 	), | ||||
| 	TP_fast_assign( | ||||
| 		PRIV_ASSIGN; | ||||
| 		__entry->offs = offs; | ||||
| 		__entry->val = val; | ||||
| 	), | ||||
| 	TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val) | ||||
| ); | ||||
| 
 | ||||
| TRACE_EVENT(iwlwifi_dev_iowrite32, | ||||
| 	TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), | ||||
| 	TP_ARGS(priv, offs, val), | ||||
| @ -48,6 +64,9 @@ TRACE_EVENT(iwlwifi_dev_iowrite32, | ||||
| 	TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val) | ||||
| ); | ||||
| 
 | ||||
| #undef TRACE_SYSTEM | ||||
| #define TRACE_SYSTEM iwlwifi | ||||
| 
 | ||||
| TRACE_EVENT(iwlwifi_dev_hcmd, | ||||
| 	TP_PROTO(struct iwl_priv *priv, void *hcmd, size_t len, u32 flags), | ||||
| 	TP_ARGS(priv, hcmd, len, flags), | ||||
|  | ||||
| @ -518,6 +518,11 @@ int iwl_eeprom_init(struct iwl_priv *priv) | ||||
| 	} | ||||
| 	e = (u16 *)priv->eeprom; | ||||
| 
 | ||||
| 	if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { | ||||
| 		/* OTP reads require powered-up chip */ | ||||
| 		priv->cfg->ops->lib->apm_ops.init(priv); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = priv->cfg->ops->lib->eeprom_ops.verify_signature(priv); | ||||
| 	if (ret < 0) { | ||||
| 		IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); | ||||
| @ -532,10 +537,8 @@ int iwl_eeprom_init(struct iwl_priv *priv) | ||||
| 		ret = -ENOENT; | ||||
| 		goto err; | ||||
| 	} | ||||
| 	if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { | ||||
| 
 | ||||
| 		/* OTP reads require powered-up chip */ | ||||
| 		priv->cfg->ops->lib->apm_ops.init(priv); | ||||
| 	if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { | ||||
| 
 | ||||
| 		ret = iwl_init_otp_access(priv); | ||||
| 		if (ret) { | ||||
| @ -751,9 +754,6 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| @ -765,7 +765,8 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, | ||||
|  *     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) | ||||
| 		struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, | ||||
| 		int element, s8 *max_txpower_in_half_dbm) | ||||
| { | ||||
| 	s8 max_txpower_avg = 0; /* (dBm) */ | ||||
| 
 | ||||
| @ -797,10 +798,14 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, | ||||
| 	    (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 | ||||
| 	/*
 | ||||
| 	 * max. tx power in EEPROM is in 1/2 dBm format | ||||
| 	 * convert from 1/2 dBm to dBm (round-up convert) | ||||
| 	 * but we also do not want to loss 1/2 dBm resolution which | ||||
| 	 * will impact performance | ||||
| 	 */ | ||||
| 	return max_txpower_avg >> 1; | ||||
| 	*max_txpower_in_half_dbm = max_txpower_avg; | ||||
| 	return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| @ -809,7 +814,7 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, | ||||
|  */ | ||||
| static s8 iwl_update_common_txpower(struct iwl_priv *priv, | ||||
| 		struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, | ||||
| 		int section, int element) | ||||
| 		int section, int element, s8 *max_txpower_in_half_dbm) | ||||
| { | ||||
| 	struct iwl_channel_info *ch_info; | ||||
| 	int ch; | ||||
| @ -823,25 +828,25 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv, | ||||
| 	if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX) | ||||
| 		is_ht40 = true; | ||||
| 	max_txpower_avg = | ||||
| 		iwl_get_max_txpower_avg(priv, enhanced_txpower, element); | ||||
| 		iwl_get_max_txpower_avg(priv, enhanced_txpower, | ||||
| 					element, max_txpower_in_half_dbm); | ||||
| 
 | ||||
| 	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)) { | ||||
| 		    (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; | ||||
| 				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++; | ||||
| 	} | ||||
| @ -854,7 +859,7 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv, | ||||
|  */ | ||||
| static s8 iwl_update_channel_txpower(struct iwl_priv *priv, | ||||
| 		struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, | ||||
| 		int section, int element) | ||||
| 		int section, int element, s8 *max_txpower_in_half_dbm) | ||||
| { | ||||
| 	struct iwl_channel_info *ch_info; | ||||
| 	int ch; | ||||
| @ -863,7 +868,8 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv, | ||||
| 
 | ||||
| 	channel = enhinfo[section].iwl_eeprom_section_channel[element]; | ||||
| 	max_txpower_avg = | ||||
| 		iwl_get_max_txpower_avg(priv, enhanced_txpower, element); | ||||
| 		iwl_get_max_txpower_avg(priv, enhanced_txpower, | ||||
| 					element, max_txpower_in_half_dbm); | ||||
| 
 | ||||
| 	ch_info = priv->channel_info; | ||||
| 	for (ch = 0; ch < priv->channel_count; ch++) { | ||||
| @ -877,12 +883,9 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv, | ||||
| 				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; | ||||
| 		} | ||||
| @ -901,6 +904,7 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv) | ||||
| 	struct iwl_eeprom_enhanced_txpwr *enhanced_txpower; | ||||
| 	u32 offset; | ||||
| 	s8 max_txpower_avg; /* (dBm) */ | ||||
| 	s8 max_txpower_in_half_dbm; /* (half-dBm) */ | ||||
| 
 | ||||
| 	/* Loop through all the sections
 | ||||
| 	 * adjust bands and channel's max tx power | ||||
| @ -913,20 +917,43 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv) | ||||
| 		enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *) | ||||
| 				iwl_eeprom_query_addr(priv, offset); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * check for valid entry - | ||||
| 		 * different version of EEPROM might contain different set | ||||
| 		 * of enhanced tx power table | ||||
| 		 * always check for valid entry before process | ||||
| 		 * the information | ||||
| 		 */ | ||||
| 		if (!enhanced_txpower->common || enhanced_txpower->reserved) | ||||
| 			continue; | ||||
| 
 | ||||
| 		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); | ||||
| 						enhanced_txpower, section, | ||||
| 						element, | ||||
| 						&max_txpower_in_half_dbm); | ||||
| 			else | ||||
| 				max_txpower_avg = | ||||
| 					iwl_update_channel_txpower(priv, | ||||
| 					enhanced_txpower, section, element); | ||||
| 						enhanced_txpower, section, | ||||
| 						element, | ||||
| 						&max_txpower_in_half_dbm); | ||||
| 
 | ||||
| 			/* 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; | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * Update the tx_power_lmt_in_half_dbm to | ||||
| 			 * the highest power supported by any channel | ||||
| 			 */ | ||||
| 			if (max_txpower_in_half_dbm > | ||||
| 			    priv->tx_power_lmt_in_half_dbm) | ||||
| 				priv->tx_power_lmt_in_half_dbm = | ||||
| 					max_txpower_in_half_dbm; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -127,19 +127,21 @@ struct iwl_eeprom_channel { | ||||
|  *    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 | ||||
|  * @common: (desc + channel) not used by driver, should _NOT_ be "zero" | ||||
|  * @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 | ||||
|  * @reserved: not used, should be "zero" | ||||
|  * @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; | ||||
| 	u16 common; | ||||
| 	s8 chain_a_max; | ||||
| 	s8 chain_b_max; | ||||
| 	s8 chain_c_max; | ||||
| 	s8 reserved1; | ||||
| 	s8 reserved; | ||||
| 	s8 mimo2_max; | ||||
| 	s8 mimo3_max; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| @ -62,6 +62,26 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static inline void _iwl_write8(struct iwl_priv *priv, u32 ofs, u8 val) | ||||
| { | ||||
| 	trace_iwlwifi_dev_iowrite8(priv, ofs, val); | ||||
| 	iowrite8(val, priv->hw_base + ofs); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| static inline void __iwl_write8(const char *f, u32 l, struct iwl_priv *priv, | ||||
| 				 u32 ofs, u8 val) | ||||
| { | ||||
| 	IWL_DEBUG_IO(priv, "write8(0x%08X, 0x%02X) - %s %d\n", ofs, val, f, l); | ||||
| 	_iwl_write8(priv, ofs, val); | ||||
| } | ||||
| #define iwl_write8(priv, ofs, val) \ | ||||
| 	__iwl_write8(__FILE__, __LINE__, priv, ofs, val) | ||||
| #else | ||||
| #define iwl_write8(priv, ofs, val) _iwl_write8(priv, ofs, val) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| static inline void _iwl_write32(struct iwl_priv *priv, u32 ofs, u32 val) | ||||
| { | ||||
| 	trace_iwlwifi_dev_iowrite32(priv, ofs, val); | ||||
|  | ||||
| @ -219,7 +219,6 @@ EXPORT_SYMBOL(iwl_leds_background); | ||||
| void iwl_leds_init(struct iwl_priv *priv) | ||||
| { | ||||
| 	priv->last_blink_rate = 0; | ||||
| 	priv->led_tpt = 0; | ||||
| 	priv->last_blink_time = 0; | ||||
| 	priv->allow_blinking = 0; | ||||
| } | ||||
|  | ||||
| @ -506,7 +506,7 @@ static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) | ||||
| { | ||||
| 	IWL_DEBUG_POWER(priv, "Prepare to enter IWL_TI_CT_KILL\n"); | ||||
| 	/* make request to retrieve statistics information */ | ||||
| 	iwl_send_statistics_request(priv, 0); | ||||
| 	iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 	/* Reschedule the ct_kill wait timer */ | ||||
| 	mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, | ||||
| 		 jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); | ||||
|  | ||||
| @ -477,7 +477,8 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) | ||||
| 			   (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| | ||||
| 			   (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); | ||||
| 
 | ||||
| 	iwl_write32(priv, CSR_INT_COALESCING, 0x40); | ||||
| 	/* Set interrupt coalescing timer to 64 x 32 = 2048 usecs */ | ||||
| 	iwl_write8(priv, CSR_INT_COALESCING, 0x40); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -635,6 +636,24 @@ void iwl_rx_statistics(struct iwl_priv *priv, | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_rx_statistics); | ||||
| 
 | ||||
| void iwl_reply_statistics(struct iwl_priv *priv, | ||||
| 			      struct iwl_rx_mem_buffer *rxb) | ||||
| { | ||||
| 	struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||||
| 
 | ||||
| 	if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { | ||||
| 		memset(&priv->statistics, 0, | ||||
| 			sizeof(struct iwl_notif_statistics)); | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| 		memset(&priv->accum_statistics, 0, | ||||
| 			sizeof(struct iwl_notif_statistics)); | ||||
| #endif | ||||
| 		IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); | ||||
| 	} | ||||
| 	iwl_rx_statistics(priv, rxb); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_reply_statistics); | ||||
| 
 | ||||
| #define PERFECT_RSSI (-20) /* dBm */ | ||||
| #define WORST_RSSI (-95)   /* dBm */ | ||||
| #define RSSI_RANGE (PERFECT_RSSI - WORST_RSSI) | ||||
| @ -1010,7 +1029,6 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, | ||||
| 	struct iwl4965_rx_mpdu_res_start *amsdu; | ||||
| 	u32 len; | ||||
| 	u32 ampdu_status; | ||||
| 	u16 fc; | ||||
| 	u32 rate_n_flags; | ||||
| 
 | ||||
| 	/**
 | ||||
| @ -1143,20 +1161,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, | ||||
| 		priv->last_tsf = le64_to_cpu(phy_res->timestamp); | ||||
| 	} | ||||
| 
 | ||||
| 	fc = le16_to_cpu(header->frame_control); | ||||
| 	switch (fc & IEEE80211_FCTL_FTYPE) { | ||||
| 	case IEEE80211_FTYPE_MGMT: | ||||
| 	case IEEE80211_FTYPE_DATA: | ||||
| 		if (priv->iw_mode == NL80211_IFTYPE_AP) | ||||
| 			iwl_update_ps_mode(priv, fc  & IEEE80211_FCTL_PM, | ||||
| 						header->addr2); | ||||
| 		/* fall through */ | ||||
| 	default: | ||||
| 		iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, | ||||
| 				rxb, &rx_status); | ||||
| 		break; | ||||
| 
 | ||||
| 	} | ||||
| 	iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, | ||||
| 				    rxb, &rx_status); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_rx_reply_rx); | ||||
| 
 | ||||
|  | ||||
| @ -1216,7 +1216,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid) | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_sta_rx_agg_stop); | ||||
| 
 | ||||
| static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) | ||||
| void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| @ -1224,27 +1224,26 @@ static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) | ||||
| 	priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK; | ||||
| 	priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; | ||||
| 	priv->stations[sta_id].sta.sta.modify_mask = 0; | ||||
| 	priv->stations[sta_id].sta.sleep_tx_count = 0; | ||||
| 	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | ||||
| 	spin_unlock_irqrestore(&priv->sta_lock, flags); | ||||
| 
 | ||||
| 	iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_sta_modify_ps_wake); | ||||
| 
 | ||||
| void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr) | ||||
| void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) | ||||
| { | ||||
| 	/* FIXME: need locking over ps_status ??? */ | ||||
| 	u8 sta_id = iwl_find_station(priv, addr); | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	if (sta_id != IWL_INVALID_STATION) { | ||||
| 		u8 sta_awake = priv->stations[sta_id]. | ||||
| 				ps_status == STA_PS_STATUS_WAKE; | ||||
| 	spin_lock_irqsave(&priv->sta_lock, flags); | ||||
| 	priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; | ||||
| 	priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; | ||||
| 	priv->stations[sta_id].sta.sta.modify_mask = | ||||
| 					STA_MODIFY_SLEEP_TX_COUNT_MSK; | ||||
| 	priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); | ||||
| 	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; | ||||
| 	spin_unlock_irqrestore(&priv->sta_lock, flags); | ||||
| 
 | ||||
| 		if (sta_awake && ps_bit) | ||||
| 			priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP; | ||||
| 		else if (!sta_awake && !ps_bit) { | ||||
| 			iwl_sta_modify_ps_wake(priv, sta_id); | ||||
| 			priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE; | ||||
| 		} | ||||
| 	} | ||||
| 	iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -66,5 +66,6 @@ void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); | ||||
| int iwl_sta_rx_agg_start(struct iwl_priv *priv, | ||||
| 			 const u8 *addr, int tid, u16 ssn); | ||||
| int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid); | ||||
| void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr); | ||||
| void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id); | ||||
| void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); | ||||
| #endif /* __iwl_sta_h__ */ | ||||
|  | ||||
| @ -710,6 +710,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_sta *sta = info->control.sta; | ||||
| 	struct iwl_station_priv *sta_priv = NULL; | ||||
| 	struct iwl_tx_queue *txq; | ||||
| 	struct iwl_queue *q; | ||||
| 	struct iwl_device_cmd *out_cmd; | ||||
| @ -772,6 +774,24 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | ||||
| 
 | ||||
| 	IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); | ||||
| 
 | ||||
| 	if (sta) | ||||
| 		sta_priv = (void *)sta->drv_priv; | ||||
| 
 | ||||
| 	if (sta_priv && sta_id != priv->hw_params.bcast_sta_id && | ||||
| 	    sta_priv->asleep) { | ||||
| 		WARN_ON(!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)); | ||||
| 		/*
 | ||||
| 		 * This sends an asynchronous command to the device, | ||||
| 		 * but we can rely on it being processed before the | ||||
| 		 * next frame is processed -- and the next frame to | ||||
| 		 * this station is the one that will consume this | ||||
| 		 * counter. | ||||
| 		 * For now set the counter to just 1 since we do not | ||||
| 		 * support uAPSD yet. | ||||
| 		 */ | ||||
| 		iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); | ||||
| 	} | ||||
| 
 | ||||
| 	txq_id = skb_get_queue_mapping(skb); | ||||
| 	if (ieee80211_is_data_qos(fc)) { | ||||
| 		qc = ieee80211_get_qos_ctl(hdr); | ||||
| @ -931,6 +951,17 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | ||||
| 	ret = iwl_txq_update_write_ptr(priv, txq); | ||||
| 	spin_unlock_irqrestore(&priv->lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * At this point the frame is "transmitted" successfully | ||||
| 	 * and we will get a TX status notification eventually, | ||||
| 	 * regardless of the value of ret. "ret" only indicates | ||||
| 	 * whether or not we should update the write pointer. | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* avoid atomic ops if it isn't an associated client */ | ||||
| 	if (sta_priv && sta_priv->client) | ||||
| 		atomic_inc(&sta_priv->pending_frames); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| @ -992,7 +1023,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) | ||||
| 	} | ||||
| 
 | ||||
| 	if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { | ||||
| 		IWL_ERR(priv, "No space for Tx\n"); | ||||
| 		IWL_ERR(priv, "No space in command queue\n"); | ||||
| 		if (iwl_within_ct_kill_margin(priv)) | ||||
| 			iwl_tt_enter_ct_kill(priv); | ||||
| 		else { | ||||
| @ -1075,6 +1106,24 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) | ||||
| 	return ret ? ret : idx; | ||||
| } | ||||
| 
 | ||||
| static void iwl_tx_status(struct iwl_priv *priv, struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||||
| 	struct ieee80211_sta *sta; | ||||
| 	struct iwl_station_priv *sta_priv; | ||||
| 
 | ||||
| 	sta = ieee80211_find_sta(priv->vif, hdr->addr1); | ||||
| 	if (sta) { | ||||
| 		sta_priv = (void *)sta->drv_priv; | ||||
| 		/* avoid atomic ops if this isn't a client */ | ||||
| 		if (sta_priv->client && | ||||
| 		    atomic_dec_return(&sta_priv->pending_frames) == 0) | ||||
| 			ieee80211_sta_block_awake(priv->hw, sta, false); | ||||
| 	} | ||||
| 
 | ||||
| 	ieee80211_tx_status_irqsafe(priv->hw, skb); | ||||
| } | ||||
| 
 | ||||
| int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) | ||||
| { | ||||
| 	struct iwl_tx_queue *txq = &priv->txq[txq_id]; | ||||
| @ -1094,7 +1143,7 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) | ||||
| 	     q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | ||||
| 
 | ||||
| 		tx_info = &txq->txb[txq->q.read_ptr]; | ||||
| 		ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0]); | ||||
| 		iwl_tx_status(priv, tx_info->skb[0]); | ||||
| 		tx_info->skb[0] = NULL; | ||||
| 
 | ||||
| 		if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl) | ||||
| @ -1264,7 +1313,7 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn) | ||||
| 	if (tid_data->tfds_in_queue == 0) { | ||||
| 		IWL_DEBUG_HT(priv, "HW queue is empty\n"); | ||||
| 		tid_data->agg.state = IWL_AGG_ON; | ||||
| 		ieee80211_start_tx_ba_cb_irqsafe(priv->hw, ra, tid); | ||||
| 		ieee80211_start_tx_ba_cb_irqsafe(priv->vif, ra, tid); | ||||
| 	} else { | ||||
| 		IWL_DEBUG_HT(priv, "HW queue is NOT empty: %d packets in HW queue\n", | ||||
| 			     tid_data->tfds_in_queue); | ||||
| @ -1329,7 +1378,7 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid); | ||||
| 	ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, ra, tid); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -1353,7 +1402,7 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) | ||||
| 			priv->cfg->ops->lib->txq_agg_disable(priv, txq_id, | ||||
| 							     ssn, tx_fifo); | ||||
| 			tid_data->agg.state = IWL_AGG_OFF; | ||||
| 			ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid); | ||||
| 			ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, addr, tid); | ||||
| 		} | ||||
| 		break; | ||||
| 	case IWL_EMPTYING_HW_QUEUE_ADDBA: | ||||
| @ -1361,7 +1410,7 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id) | ||||
| 		if (tid_data->tfds_in_queue == 0) { | ||||
| 			IWL_DEBUG_HT(priv, "HW queue empty: continue ADDBA flow\n"); | ||||
| 			tid_data->agg.state = IWL_AGG_ON; | ||||
| 			ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid); | ||||
| 			ieee80211_start_tx_ba_cb_irqsafe(priv->vif, addr, tid); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| @ -1483,7 +1483,6 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv) | ||||
| 	tasklet_kill(&priv->irq_tasklet); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| static const char *desc_lookup(int i) | ||||
| { | ||||
| 	switch (i) { | ||||
| @ -1614,10 +1613,42 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, | ||||
| 	spin_unlock_irqrestore(&priv->reg_lock, reg_flags); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog | ||||
|  */ | ||||
| static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity, | ||||
| 				      u32 num_wraps, u32 next_entry, | ||||
| 				      u32 size, u32 mode) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * display the newest DEFAULT_LOG_ENTRIES entries | ||||
| 	 * i.e the entries just before the next ont that uCode would fill. | ||||
| 	 */ | ||||
| 	if (num_wraps) { | ||||
| 		if (next_entry < size) { | ||||
| 			iwl3945_print_event_log(priv, | ||||
| 					capacity - (size - next_entry), | ||||
| 					size - next_entry, mode); | ||||
| 			iwl3945_print_event_log(priv, 0, | ||||
| 				    next_entry, mode); | ||||
| 		} else | ||||
| 			iwl3945_print_event_log(priv, next_entry - size, | ||||
| 				    size, mode); | ||||
| 	} else { | ||||
| 		if (next_entry < size) | ||||
| 			iwl3945_print_event_log(priv, 0, next_entry, mode); | ||||
| 		else | ||||
| 			iwl3945_print_event_log(priv, next_entry - size, | ||||
| 					    size, mode); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* For sanity check only.  Actual size is determined by uCode, typ. 512 */ | ||||
| #define IWL3945_MAX_EVENT_LOG_SIZE (512) | ||||
| 
 | ||||
| void iwl3945_dump_nic_event_log(struct iwl_priv *priv) | ||||
| #define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20) | ||||
| 
 | ||||
| void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log) | ||||
| { | ||||
| 	u32 base;       /* SRAM byte address of event log header */ | ||||
| 	u32 capacity;   /* event log capacity in # entries */ | ||||
| @ -1658,8 +1689,17 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", | ||||
| 		  size, num_wraps); | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| 	if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)) | ||||
| 		size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES) | ||||
| 			? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size; | ||||
| #else | ||||
| 	size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES) | ||||
| 		? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size; | ||||
| #endif | ||||
| 
 | ||||
| 	IWL_ERR(priv, "Start IWL Event Log Dump: display last %d count\n", | ||||
| 		  size); | ||||
| 
 | ||||
| 	/* if uCode has wrapped back to top of log, start at the oldest entry,
 | ||||
| 	 * i.e the next one that uCode would fill. */ | ||||
| @ -1670,18 +1710,28 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv) | ||||
| 	/* (then/else) start at top of log */ | ||||
| 	iwl3945_print_event_log(priv, 0, next_entry, mode); | ||||
| 
 | ||||
| } | ||||
| #ifdef CONFIG_IWLWIFI_DEBUG | ||||
| 	if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { | ||||
| 		/* 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) | ||||
| 			iwl3945_print_event_log(priv, next_entry, | ||||
| 				    capacity - next_entry, mode); | ||||
| 
 | ||||
| 		/* (then/else) start at top of log */ | ||||
| 		iwl3945_print_event_log(priv, 0, next_entry, mode); | ||||
| 	} else | ||||
| 		iwl3945_print_last_event_logs(priv, capacity, num_wraps, | ||||
| 					next_entry, size, mode); | ||||
| #else | ||||
| void iwl3945_dump_nic_event_log(struct iwl_priv *priv) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void iwl3945_dump_nic_error_log(struct iwl_priv *priv) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| 	iwl3945_print_last_event_logs(priv, capacity, num_wraps, | ||||
| 				next_entry, size, mode); | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static void iwl3945_irq_tasklet(struct iwl_priv *priv) | ||||
| { | ||||
| 	u32 inta, handled = 0; | ||||
| @ -2494,7 +2544,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv) | ||||
| 	priv->active_rate = priv->rates_mask; | ||||
| 	priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; | ||||
| 
 | ||||
| 	iwl_power_update_mode(priv, false); | ||||
| 	iwl_power_update_mode(priv, true); | ||||
| 
 | ||||
| 	if (iwl_is_associated(priv)) { | ||||
| 		struct iwl3945_rxon_cmd *active_rxon = | ||||
| @ -3650,7 +3700,7 @@ static ssize_t show_statistics(struct device *d, | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 	rc = iwl_send_statistics_request(priv, 0); | ||||
| 	rc = iwl_send_statistics_request(priv, CMD_SYNC, false); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	if (rc) { | ||||
| @ -3905,10 +3955,8 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) | ||||
| 		BIT(NL80211_IFTYPE_STATION) | | ||||
| 		BIT(NL80211_IFTYPE_ADHOC); | ||||
| 
 | ||||
| 	hw->wiphy->custom_regulatory = true; | ||||
| 
 | ||||
| 	/* Firmware does not support this */ | ||||
| 	hw->wiphy->disable_beacon_hints = true; | ||||
| 	hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY | | ||||
| 			    WIPHY_FLAG_DISABLE_BEACON_HINTS; | ||||
| 
 | ||||
| 	hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945; | ||||
| 	/* we create the 802.11 header and a zero-length SSID element */ | ||||
|  | ||||
| @ -1146,46 +1146,46 @@ static int __init init_mac80211_hwsim(void) | ||||
| 			break; | ||||
| 		case HWSIM_REGTEST_WORLD_ROAM: | ||||
| 			if (i == 0) { | ||||
| 				hw->wiphy->custom_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 				wiphy_apply_custom_regulatory(hw->wiphy, | ||||
| 					&hwsim_world_regdom_custom_01); | ||||
| 			} | ||||
| 			break; | ||||
| 		case HWSIM_REGTEST_CUSTOM_WORLD: | ||||
| 			hw->wiphy->custom_regulatory = true; | ||||
| 			hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 			wiphy_apply_custom_regulatory(hw->wiphy, | ||||
| 				&hwsim_world_regdom_custom_01); | ||||
| 			break; | ||||
| 		case HWSIM_REGTEST_CUSTOM_WORLD_2: | ||||
| 			if (i == 0) { | ||||
| 				hw->wiphy->custom_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 				wiphy_apply_custom_regulatory(hw->wiphy, | ||||
| 					&hwsim_world_regdom_custom_01); | ||||
| 			} else if (i == 1) { | ||||
| 				hw->wiphy->custom_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 				wiphy_apply_custom_regulatory(hw->wiphy, | ||||
| 					&hwsim_world_regdom_custom_02); | ||||
| 			} | ||||
| 			break; | ||||
| 		case HWSIM_REGTEST_STRICT_ALL: | ||||
| 			hw->wiphy->strict_regulatory = true; | ||||
| 			hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; | ||||
| 			break; | ||||
| 		case HWSIM_REGTEST_STRICT_FOLLOW: | ||||
| 		case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: | ||||
| 			if (i == 0) | ||||
| 				hw->wiphy->strict_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; | ||||
| 			break; | ||||
| 		case HWSIM_REGTEST_ALL: | ||||
| 			if (i == 0) { | ||||
| 				hw->wiphy->custom_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 				wiphy_apply_custom_regulatory(hw->wiphy, | ||||
| 					&hwsim_world_regdom_custom_01); | ||||
| 			} else if (i == 1) { | ||||
| 				hw->wiphy->custom_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||||
| 				wiphy_apply_custom_regulatory(hw->wiphy, | ||||
| 					&hwsim_world_regdom_custom_02); | ||||
| 			} else if (i == 4) | ||||
| 				hw->wiphy->strict_regulatory = true; | ||||
| 				hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
|  | ||||
| @ -579,7 +579,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) | ||||
| 	 * For now, disable PS by default because it affects | ||||
| 	 * link stability significantly. | ||||
| 	 */ | ||||
| 	dev->wiphy->ps_default = false; | ||||
| 	dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; | ||||
| 
 | ||||
| 	mutex_init(&priv->conf_mutex); | ||||
| 	mutex_init(&priv->eeprom_mutex); | ||||
|  | ||||
| @ -83,11 +83,11 @@ MODULE_PARM_DESC(roamdelta, | ||||
| 	"set roaming tendency: 0=aggressive, 1=moderate, " | ||||
| 				"2=conservative (default: moderate)"); | ||||
| 
 | ||||
| static int modparam_workaround_interval = 500; | ||||
| static int modparam_workaround_interval; | ||||
| module_param_named(workaround_interval, modparam_workaround_interval, | ||||
| 							int, 0444); | ||||
| MODULE_PARM_DESC(workaround_interval, | ||||
| 	"set stall workaround interval in msecs (default: 500)"); | ||||
| 	"set stall workaround interval in msecs (0=disabled) (default: 0)"); | ||||
| 
 | ||||
| 
 | ||||
| /* various RNDIS OID defs */ | ||||
| @ -733,12 +733,13 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) | ||||
| 			le32_to_cpu(u.get_c->status)); | ||||
| 
 | ||||
| 	if (ret == 0) { | ||||
| 		memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len); | ||||
| 
 | ||||
| 		ret = le32_to_cpu(u.get_c->len); | ||||
| 		if (ret > *len) | ||||
| 			*len = ret; | ||||
| 		memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len); | ||||
| 		ret = rndis_error_status(u.get_c->status); | ||||
| 
 | ||||
| 		ret = rndis_error_status(u.get_c->status); | ||||
| 		if (ret < 0) | ||||
| 			devdbg(dev, "rndis_query_oid(%s): device returned " | ||||
| 				"error,  0x%08x (%d)", oid_to_string(oid), | ||||
| @ -2549,7 +2550,7 @@ static void rndis_device_poller(struct work_struct *work) | ||||
| 	/* Workaround transfer stalls on poor quality links.
 | ||||
| 	 * TODO: find right way to fix these stalls (as stalls do not happen | ||||
| 	 * with ndiswrapper/windows driver). */ | ||||
| 	if (priv->last_qual <= 25) { | ||||
| 	if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) { | ||||
| 		/* Decrease stats worker interval to catch stalls.
 | ||||
| 		 * faster. Faster than 400-500ms causes packet loss, | ||||
| 		 * Slower doesn't catch stalls fast enough. | ||||
|  | ||||
| @ -824,17 +824,23 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x0e0b, 0x9041), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Amit */ | ||||
| 	{ USB_DEVICE(0x15c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Askey */ | ||||
| 	{ USB_DEVICE(0x1690, 0x0740), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1690, 0x0744), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* ASUS */ | ||||
| 	{ USB_DEVICE(0x0b05, 0x1731), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0b05, 0x1732), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0b05, 0x1742), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0b05, 0x1760), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0b05, 0x1761), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0b05, 0x1784), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* AzureWave */ | ||||
| 	{ USB_DEVICE(0x13d3, 0x3247), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x13d3, 0x3273), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x13d3, 0x3305), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Belkin */ | ||||
| 	{ USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -843,6 +849,8 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	/* Buffalo */ | ||||
| 	{ USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Cisco */ | ||||
| 	{ USB_DEVICE(0x167b, 0x4001), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Conceptronic */ | ||||
| 	{ USB_DEVICE(0x14b2, 0x3c06), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x14b2, 0x3c07), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -858,6 +866,8 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x07aa, 0x002f), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07aa, 0x003c), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07aa, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07aa, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07aa, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x18c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x18c5, 0x0012), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* D-Link */ | ||||
| @ -869,18 +879,24 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x07d1, 0x3c0f), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07d1, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Edimax */ | ||||
| 	{ USB_DEVICE(0x7392, 0x7711), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x7392, 0x7717), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x7392, 0x7718), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Encore */ | ||||
| 	{ USB_DEVICE(0x203d, 0x1480), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x203d, 0x14a9), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* EnGenius */ | ||||
| 	{ USB_DEVICE(0X1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9702), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9703), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9705), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9706), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9707), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9708), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9709), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x9801), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Gemtek */ | ||||
| 	{ USB_DEVICE(0x15a9, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -894,7 +910,10 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x0e66, 0x0009), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0e66, 0x000b), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* I-O DATA */ | ||||
| 	{ USB_DEVICE(0x04bb, 0x0944), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x04bb, 0x0945), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x04bb, 0x0947), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x04bb, 0x0948), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* LevelOne */ | ||||
| 	{ USB_DEVICE(0x1740, 0x0605), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1740, 0x0615), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -909,8 +928,18 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	/* Motorola */ | ||||
| 	{ USB_DEVICE(0x100d, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x100d, 0x9032), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* MSI */ | ||||
| 	{ USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x3821), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x3870), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x821a), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x870a), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x899a), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Ovislink */ | ||||
| 	{ USB_DEVICE(0x1b75, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Para */ | ||||
| 	{ USB_DEVICE(0x20b8, 0x8888), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Pegatron */ | ||||
| 	{ USB_DEVICE(0x1d4d, 0x0002), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x1d4d, 0x000c), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -926,8 +955,6 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	/* Quanta */ | ||||
| 	{ USB_DEVICE(0x1a32, 0x0304), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Ralink */ | ||||
| 	{ USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x148f, 0x2070), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x148f, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x148f, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -951,7 +978,12 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x0df6, 0x003e), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x0040), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x0047), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x0048), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x004a), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x0df6, 0x004d), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* SMC */ | ||||
| 	{ USB_DEVICE(0x083a, 0x6618), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0x7511), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| @ -960,6 +992,8 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x083a, 0x8522), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0xa512), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0xa618), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0xa701), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0xa702), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0xb522), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Sparklan */ | ||||
| @ -977,6 +1011,7 @@ static struct usb_device_id rt2800usb_device_table[] = { | ||||
| 	{ USB_DEVICE(0x5a57, 0x0280), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x5a57, 0x0282), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x5a57, 0x0283), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x5a57, 0x0284), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	{ USB_DEVICE(0x5a57, 0x5257), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
| 	/* Zyxel */ | ||||
| 	{ USB_DEVICE(0x0586, 0x3416), USB_DEVICE_DATA(&rt2800usb_ops) }, | ||||
|  | ||||
| @ -205,6 +205,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, | ||||
| 	enum data_queue_qid qid = skb_get_queue_mapping(entry->skb); | ||||
| 	unsigned int header_length = ieee80211_get_hdrlen_from_skb(entry->skb); | ||||
| 	u8 rate_idx, rate_flags, retry_rates; | ||||
| 	u8 skbdesc_flags = skbdesc->flags; | ||||
| 	unsigned int i; | ||||
| 	bool success; | ||||
| 
 | ||||
| @ -287,12 +288,12 @@ void rt2x00lib_txdone(struct queue_entry *entry, | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Only send the status report to mac80211 when TX status was | ||||
| 	 * requested by it. If this was a extra frame coming through | ||||
| 	 * a mac80211 library call (RTS/CTS) then we should not send the | ||||
| 	 * status report back. | ||||
| 	 * Only send the status report to mac80211 when it's a frame | ||||
| 	 * that originated in mac80211. If this was a extra frame coming | ||||
| 	 * through a mac80211 library call (RTS/CTS) then we should not | ||||
| 	 * send the status report back. | ||||
| 	 */ | ||||
| 	if (tx_info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) | ||||
| 	if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) | ||||
| 		ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb); | ||||
| 	else | ||||
| 		dev_kfree_skb_irq(entry->skb); | ||||
|  | ||||
| @ -162,8 +162,10 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); | ||||
|  * rt2x00queue_write_tx_frame - Write TX frame to hardware | ||||
|  * @queue: Queue over which the frame should be send | ||||
|  * @skb: The skb to send | ||||
|  * @local: frame is not from mac80211 | ||||
|  */ | ||||
| int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb); | ||||
| int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, | ||||
| 			       bool local); | ||||
| 
 | ||||
| /**
 | ||||
|  * rt2x00queue_update_beacon - Send new beacon from mac80211 to hardware | ||||
|  | ||||
| @ -66,7 +66,6 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, | ||||
| 	rts_info = IEEE80211_SKB_CB(skb); | ||||
| 	rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; | ||||
| 	rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT; | ||||
| 	rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS; | ||||
| 
 | ||||
| 	if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) | ||||
| 		rts_info->flags |= IEEE80211_TX_CTL_NO_ACK; | ||||
| @ -91,7 +90,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, | ||||
| 				  frag_skb->data, data_length, tx_info, | ||||
| 				  (struct ieee80211_rts *)(skb->data)); | ||||
| 
 | ||||
| 	retval = rt2x00queue_write_tx_frame(queue, skb); | ||||
| 	retval = rt2x00queue_write_tx_frame(queue, skb, true); | ||||
| 	if (retval) { | ||||
| 		dev_kfree_skb_any(skb); | ||||
| 		WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); | ||||
| @ -153,7 +152,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | ||||
| 			goto exit_fail; | ||||
| 	} | ||||
| 
 | ||||
| 	if (rt2x00queue_write_tx_frame(queue, skb)) | ||||
| 	if (rt2x00queue_write_tx_frame(queue, skb, false)) | ||||
| 		goto exit_fail; | ||||
| 
 | ||||
| 	if (rt2x00queue_threshold(queue)) | ||||
|  | ||||
| @ -454,7 +454,8 @@ static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry, | ||||
| 		rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, queue->qid); | ||||
| } | ||||
| 
 | ||||
| int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) | ||||
| int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, | ||||
| 			       bool local) | ||||
| { | ||||
| 	struct ieee80211_tx_info *tx_info; | ||||
| 	struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); | ||||
| @ -495,6 +496,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) | ||||
| 	skbdesc->tx_rate_idx = rate_idx; | ||||
| 	skbdesc->tx_rate_flags = rate_flags; | ||||
| 
 | ||||
| 	if (local) | ||||
| 		skbdesc->flags |= SKBDESC_NOT_MAC80211; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * When hardware encryption is supported, and this frame | ||||
| 	 * is to be encrypted, we should strip the IV/EIV data from | ||||
|  | ||||
| @ -94,12 +94,15 @@ enum data_queue_qid { | ||||
|  *	mac80211 but was stripped for processing by the driver. | ||||
|  * @SKBDESC_L2_PADDED: Payload has been padded for 4-byte alignment, | ||||
|  *	the padded bytes are located between header and payload. | ||||
|  * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, | ||||
|  *	don't try to pass it back. | ||||
|  */ | ||||
| enum skb_frame_desc_flags { | ||||
| 	SKBDESC_DMA_MAPPED_RX = 1 << 0, | ||||
| 	SKBDESC_DMA_MAPPED_TX = 1 << 1, | ||||
| 	SKBDESC_IV_STRIPPED = 1 << 2, | ||||
| 	SKBDESC_L2_PADDED = 1 << 3 | ||||
| 	SKBDESC_L2_PADDED = 1 << 3, | ||||
| 	SKBDESC_NOT_MAC80211 = 1 << 4, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
| @ -269,6 +269,7 @@ struct wl1251 { | ||||
| 
 | ||||
| 	void (*set_power)(bool enable); | ||||
| 	int irq; | ||||
| 	bool use_eeprom; | ||||
| 
 | ||||
| 	enum wl1251_state state; | ||||
| 	struct mutex mutex; | ||||
| @ -354,6 +355,8 @@ struct wl1251 { | ||||
| 	/* is firmware in elp mode */ | ||||
| 	bool elp; | ||||
| 
 | ||||
| 	struct delayed_work elp_work; | ||||
| 
 | ||||
| 	/* we can be in psm, but not in elp, we have to differentiate */ | ||||
| 	bool psm; | ||||
| 
 | ||||
| @ -374,6 +377,8 @@ struct wl1251 { | ||||
| 	u8 buffer_busyword[WL1251_BUSY_WORD_LEN]; | ||||
| 	struct wl1251_rx_descriptor *rx_descriptor; | ||||
| 
 | ||||
| 	struct ieee80211_vif *vif; | ||||
| 
 | ||||
| 	u32 chip_id; | ||||
| 	char fw_ver[21]; | ||||
| }; | ||||
|  | ||||
| @ -494,7 +494,7 @@ out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) | ||||
| int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter) | ||||
| { | ||||
| 	struct acx_beacon_filter_option *beacon_filter; | ||||
| 	int ret; | ||||
| @ -507,7 +507,7 @@ int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	beacon_filter->enable = 0; | ||||
| 	beacon_filter->enable = enable_filter; | ||||
| 	beacon_filter->max_num_beacons = 0; | ||||
| 
 | ||||
| 	ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT, | ||||
| @ -525,6 +525,7 @@ out: | ||||
| int wl1251_acx_beacon_filter_table(struct wl1251 *wl) | ||||
| { | ||||
| 	struct acx_beacon_filter_ie_table *ie_table; | ||||
| 	int idx = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_ACX, "acx beacon filter table"); | ||||
| @ -535,8 +536,10 @@ int wl1251_acx_beacon_filter_table(struct wl1251 *wl) | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ie_table->num_ie = 0; | ||||
| 	memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); | ||||
| 	/* configure default beacon pass-through rules */ | ||||
| 	ie_table->num_ie = 1; | ||||
| 	ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN; | ||||
| 	ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE; | ||||
| 
 | ||||
| 	ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, | ||||
| 				   ie_table, sizeof(*ie_table)); | ||||
| @ -550,6 +553,35 @@ out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wl1251_acx_conn_monit_params(struct wl1251 *wl) | ||||
| { | ||||
| 	struct acx_conn_monit_params *acx; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_ACX, "acx connection monitor parameters"); | ||||
| 
 | ||||
| 	acx = kzalloc(sizeof(*acx), GFP_KERNEL); | ||||
| 	if (!acx) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD; | ||||
| 	acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT; | ||||
| 
 | ||||
| 	ret = wl1251_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, | ||||
| 				   acx, sizeof(*acx)); | ||||
| 	if (ret < 0) { | ||||
| 		wl1251_warning("failed to set connection monitor " | ||||
| 			       "parameters: %d", ret); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	kfree(acx); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wl1251_acx_sg_enable(struct wl1251 *wl) | ||||
| { | ||||
| 	struct acx_bt_wlan_coex *pta; | ||||
| @ -916,3 +948,31 @@ out: | ||||
| 	kfree(mem_conf); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim) | ||||
| { | ||||
| 	struct wl1251_acx_wr_tbtt_and_dtim *acx; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_ACX, "acx tbtt and dtim"); | ||||
| 
 | ||||
| 	acx = kzalloc(sizeof(*acx), GFP_KERNEL); | ||||
| 	if (!acx) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	acx->tbtt = tbtt; | ||||
| 	acx->dtim = dtim; | ||||
| 
 | ||||
| 	ret = wl1251_cmd_configure(wl, ACX_WR_TBTT_AND_DTIM, | ||||
| 				   acx, sizeof(*acx)); | ||||
| 	if (ret < 0) { | ||||
| 		wl1251_warning("failed to set tbtt and dtim: %d", ret); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	kfree(acx); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @ -450,6 +450,11 @@ struct acx_beacon_filter_option { | ||||
| 			   (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \ | ||||
| 			    BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE)) | ||||
| 
 | ||||
| #define BEACON_RULE_PASS_ON_CHANGE                     BIT(0) | ||||
| #define BEACON_RULE_PASS_ON_APPEARANCE                 BIT(1) | ||||
| 
 | ||||
| #define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN         (37) | ||||
| 
 | ||||
| struct acx_beacon_filter_ie_table { | ||||
| 	struct acx_header header; | ||||
| 
 | ||||
| @ -458,6 +463,16 @@ struct acx_beacon_filter_ie_table { | ||||
| 	u8 pad[3]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| #define SYNCH_FAIL_DEFAULT_THRESHOLD    10     /* number of beacons */ | ||||
| #define NO_BEACON_DEFAULT_TIMEOUT       (500) /* in microseconds */ | ||||
| 
 | ||||
| struct acx_conn_monit_params { | ||||
| 	struct acx_header header; | ||||
| 
 | ||||
| 	u32 synch_fail_thold; /* number of beacons missed */ | ||||
| 	u32 bss_lose_timeout; /* number of TU's from synch fail */ | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	SG_ENABLE = 0, | ||||
| 	SG_DISABLE, | ||||
| @ -1134,6 +1149,23 @@ struct wl1251_acx_mem_map { | ||||
| 	u32 num_rx_mem_blocks; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| struct wl1251_acx_wr_tbtt_and_dtim { | ||||
| 
 | ||||
| 	struct acx_header header; | ||||
| 
 | ||||
| 	/* Time in TUs between two consecutive beacons */ | ||||
| 	u16 tbtt; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * DTIM period | ||||
| 	 * For BSS: Number of TBTTs in a DTIM period (range: 1-10) | ||||
| 	 * For IBSS: value shall be set to 1 | ||||
| 	*/ | ||||
| 	u8  dtim; | ||||
| 	u8  padding; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
| 
 | ||||
|     Host Interrupt Register (WiLink -> Host) | ||||
| @ -1273,8 +1305,9 @@ int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time); | ||||
| int wl1251_acx_group_address_tbl(struct wl1251 *wl); | ||||
| int wl1251_acx_service_period_timeout(struct wl1251 *wl); | ||||
| int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold); | ||||
| int wl1251_acx_beacon_filter_opt(struct wl1251 *wl); | ||||
| int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter); | ||||
| int wl1251_acx_beacon_filter_table(struct wl1251 *wl); | ||||
| int wl1251_acx_conn_monit_params(struct wl1251 *wl); | ||||
| int wl1251_acx_sg_enable(struct wl1251 *wl); | ||||
| int wl1251_acx_sg_cfg(struct wl1251 *wl); | ||||
| int wl1251_acx_cca_threshold(struct wl1251 *wl); | ||||
| @ -1288,5 +1321,6 @@ int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats); | ||||
| int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); | ||||
| int wl1251_acx_rate_policies(struct wl1251 *wl); | ||||
| int wl1251_acx_mem_cfg(struct wl1251 *wl); | ||||
| int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); | ||||
| 
 | ||||
| #endif /* __WL1251_ACX_H__ */ | ||||
|  | ||||
| @ -296,8 +296,12 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) | ||||
| 		WL1251_ACX_INTR_INIT_COMPLETE; | ||||
| 	wl1251_boot_target_enable_interrupts(wl); | ||||
| 
 | ||||
| 	/* unmask all mbox events  */ | ||||
| 	wl->event_mask = 0xffffffff; | ||||
| 	wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID | | ||||
| 		SYNCHRONIZATION_TIMEOUT_EVENT_ID | | ||||
| 		ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | | ||||
| 		ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | | ||||
| 		REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | | ||||
| 		BT_PTA_PREDICTION_EVENT_ID; | ||||
| 
 | ||||
| 	ret = wl1251_event_unmask(wl); | ||||
| 	if (ret < 0) { | ||||
| @ -314,8 +318,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) | ||||
| static int wl1251_boot_upload_firmware(struct wl1251 *wl) | ||||
| { | ||||
| 	int addr, chunk_num, partition_limit; | ||||
| 	size_t fw_data_len; | ||||
| 	u8 *p; | ||||
| 	size_t fw_data_len, len; | ||||
| 	u8 *p, *buf; | ||||
| 
 | ||||
| 	/* whal_FwCtrl_LoadFwImageSm() */ | ||||
| 
 | ||||
| @ -334,6 +338,12 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	buf = kmalloc(CHUNK_SIZE, GFP_KERNEL); | ||||
| 	if (!buf) { | ||||
| 		wl1251_error("allocation for firmware upload chunk failed"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, | ||||
| 			     WL1251_PART_DOWN_MEM_SIZE, | ||||
| 			     WL1251_PART_DOWN_REG_START, | ||||
| @ -364,7 +374,11 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) | ||||
| 		p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; | ||||
| 		wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", | ||||
| 			     p, addr); | ||||
| 		wl1251_mem_write(wl, addr, p, CHUNK_SIZE); | ||||
| 
 | ||||
| 		/* need to copy the chunk for dma */ | ||||
| 		len = CHUNK_SIZE; | ||||
| 		memcpy(buf, p, len); | ||||
| 		wl1251_mem_write(wl, addr, buf, len); | ||||
| 
 | ||||
| 		chunk_num++; | ||||
| 	} | ||||
| @ -372,9 +386,16 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl) | ||||
| 	/* 10.4 upload the last chunk */ | ||||
| 	addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; | ||||
| 	p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; | ||||
| 
 | ||||
| 	/* need to copy the chunk for dma */ | ||||
| 	len = fw_data_len % CHUNK_SIZE; | ||||
| 	memcpy(buf, p, len); | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", | ||||
| 		     fw_data_len % CHUNK_SIZE, p, addr); | ||||
| 	wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); | ||||
| 		     len, p, addr); | ||||
| 	wl1251_mem_write(wl, addr, buf, len); | ||||
| 
 | ||||
| 	kfree(buf); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -473,13 +494,19 @@ int wl1251_boot(struct wl1251 *wl) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* 2. start processing NVS file */ | ||||
| 	ret = wl1251_boot_upload_nvs(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 	if (wl->use_eeprom) { | ||||
| 		wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR); | ||||
| 		msleep(4000); | ||||
| 		wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM); | ||||
| 	} else { | ||||
| 		ret = wl1251_boot_upload_nvs(wl); | ||||
| 		if (ret < 0) | ||||
| 			goto out; | ||||
| 
 | ||||
| 	/* write firmware's last address (ie. it's length) to
 | ||||
| 	 * ACX_EEPROMLESS_IND_REG */ | ||||
| 	wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); | ||||
| 		/* write firmware's last address (ie. it's length) to
 | ||||
| 		 * ACX_EEPROMLESS_IND_REG */ | ||||
| 		wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); | ||||
| 	} | ||||
| 
 | ||||
| 	/* 6. read the EEPROM parameters */ | ||||
| 	tmp = wl1251_reg_read32(wl, SCR_PAD2); | ||||
|  | ||||
| @ -79,6 +79,21 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID && wl->psm) { | ||||
| 		wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); | ||||
| 
 | ||||
| 		/* indicate to the stack, that beacons have been lost */ | ||||
| 		ieee80211_beacon_loss(wl->vif); | ||||
| 	} | ||||
| 
 | ||||
| 	if (vector & REGAINED_BSS_EVENT_ID) { | ||||
| 		if (wl->psm_requested) { | ||||
| 			ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -147,7 +147,8 @@ int wl1251_hw_init_beacon_filter(struct wl1251 *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl1251_acx_beacon_filter_opt(wl); | ||||
| 	/* disable beacon filtering at this stage */ | ||||
| 	ret = wl1251_acx_beacon_filter_opt(wl, false); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| @ -364,6 +365,11 @@ int wl1251_hw_init(struct wl1251 *wl) | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Initialize connection monitoring thresholds */ | ||||
| 	ret = wl1251_acx_conn_monit_params(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Beacon filtering */ | ||||
| 	ret = wl1251_hw_init_beacon_filter(wl); | ||||
| 	if (ret < 0) | ||||
|  | ||||
| @ -28,6 +28,7 @@ | ||||
| #include <linux/irq.h> | ||||
| #include <linux/crc32.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/vmalloc.h> | ||||
| 
 | ||||
| #include "wl1251.h" | ||||
| #include "wl12xx_80211.h" | ||||
| @ -83,7 +84,7 @@ static int wl1251_fetch_firmware(struct wl1251 *wl) | ||||
| 	} | ||||
| 
 | ||||
| 	wl->fw_len = fw->size; | ||||
| 	wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); | ||||
| 	wl->fw = vmalloc(wl->fw_len); | ||||
| 
 | ||||
| 	if (!wl->fw) { | ||||
| 		wl1251_error("could not allocate memory for the firmware"); | ||||
| @ -211,9 +212,10 @@ out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #define WL1251_IRQ_LOOP_COUNT 10 | ||||
| static void wl1251_irq_work(struct work_struct *work) | ||||
| { | ||||
| 	u32 intr; | ||||
| 	u32 intr, ctr = WL1251_IRQ_LOOP_COUNT; | ||||
| 	struct wl1251 *wl = | ||||
| 		container_of(work, struct wl1251, irq_work); | ||||
| 	int ret; | ||||
| @ -234,78 +236,86 @@ static void wl1251_irq_work(struct work_struct *work) | ||||
| 	intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); | ||||
| 	wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); | ||||
| 
 | ||||
| 	if (wl->data_path) { | ||||
| 		wl->rx_counter = | ||||
| 			wl1251_mem_read32(wl, wl->data_path->rx_control_addr); | ||||
| 	do { | ||||
| 		if (wl->data_path) { | ||||
| 			wl->rx_counter = wl1251_mem_read32( | ||||
| 				wl, wl->data_path->rx_control_addr); | ||||
| 
 | ||||
| 		/* We handle a frmware bug here */ | ||||
| 		switch ((wl->rx_counter - wl->rx_handled) & 0xf) { | ||||
| 		case 0: | ||||
| 			wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync"); | ||||
| 			intr &= ~WL1251_ACX_INTR_RX0_DATA; | ||||
| 			intr &= ~WL1251_ACX_INTR_RX1_DATA; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			wl1251_debug(DEBUG_IRQ, "RX: FW +1"); | ||||
| 			intr |= WL1251_ACX_INTR_RX0_DATA; | ||||
| 			intr &= ~WL1251_ACX_INTR_RX1_DATA; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			wl1251_debug(DEBUG_IRQ, "RX: FW +2"); | ||||
| 			intr |= WL1251_ACX_INTR_RX0_DATA; | ||||
| 			intr |= WL1251_ACX_INTR_RX1_DATA; | ||||
| 			break; | ||||
| 		default: | ||||
| 			wl1251_warning("RX: FW and host out of sync: %d", | ||||
| 				       wl->rx_counter - wl->rx_handled); | ||||
| 			break; | ||||
| 			/* We handle a frmware bug here */ | ||||
| 			switch ((wl->rx_counter - wl->rx_handled) & 0xf) { | ||||
| 			case 0: | ||||
| 				wl1251_debug(DEBUG_IRQ, | ||||
| 					     "RX: FW and host in sync"); | ||||
| 				intr &= ~WL1251_ACX_INTR_RX0_DATA; | ||||
| 				intr &= ~WL1251_ACX_INTR_RX1_DATA; | ||||
| 				break; | ||||
| 			case 1: | ||||
| 				wl1251_debug(DEBUG_IRQ, "RX: FW +1"); | ||||
| 				intr |= WL1251_ACX_INTR_RX0_DATA; | ||||
| 				intr &= ~WL1251_ACX_INTR_RX1_DATA; | ||||
| 				break; | ||||
| 			case 2: | ||||
| 				wl1251_debug(DEBUG_IRQ, "RX: FW +2"); | ||||
| 				intr |= WL1251_ACX_INTR_RX0_DATA; | ||||
| 				intr |= WL1251_ACX_INTR_RX1_DATA; | ||||
| 				break; | ||||
| 			default: | ||||
| 				wl1251_warning( | ||||
| 					"RX: FW and host out of sync: %d", | ||||
| 					wl->rx_counter - wl->rx_handled); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			wl->rx_handled = wl->rx_counter; | ||||
| 
 | ||||
| 			wl1251_debug(DEBUG_IRQ, "RX counter: %d", | ||||
| 				     wl->rx_counter); | ||||
| 		} | ||||
| 
 | ||||
| 		wl->rx_handled = wl->rx_counter; | ||||
| 		intr &= wl->intr_mask; | ||||
| 
 | ||||
| 		if (intr == 0) { | ||||
| 			wl1251_debug(DEBUG_IRQ, "INTR is 0"); | ||||
| 			goto out_sleep; | ||||
| 		} | ||||
| 
 | ||||
| 		wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); | ||||
| 	} | ||||
| 		if (intr & WL1251_ACX_INTR_RX0_DATA) { | ||||
| 			wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); | ||||
| 			wl1251_rx(wl); | ||||
| 		} | ||||
| 
 | ||||
| 	intr &= wl->intr_mask; | ||||
| 		if (intr & WL1251_ACX_INTR_RX1_DATA) { | ||||
| 			wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); | ||||
| 			wl1251_rx(wl); | ||||
| 		} | ||||
| 
 | ||||
| 	if (intr == 0) { | ||||
| 		wl1251_debug(DEBUG_IRQ, "INTR is 0"); | ||||
| 		wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, | ||||
| 				   ~(wl->intr_mask)); | ||||
| 		if (intr & WL1251_ACX_INTR_TX_RESULT) { | ||||
| 			wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); | ||||
| 			wl1251_tx_complete(wl); | ||||
| 		} | ||||
| 
 | ||||
| 		goto out_sleep; | ||||
| 	} | ||||
| 		if (intr & (WL1251_ACX_INTR_EVENT_A | | ||||
| 			    WL1251_ACX_INTR_EVENT_B)) { | ||||
| 			wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", | ||||
| 				     intr); | ||||
| 			if (intr & WL1251_ACX_INTR_EVENT_A) | ||||
| 				wl1251_event_handle(wl, 0); | ||||
| 			else | ||||
| 				wl1251_event_handle(wl, 1); | ||||
| 		} | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_RX0_DATA) { | ||||
| 		wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); | ||||
| 		wl1251_rx(wl); | ||||
| 	} | ||||
| 		if (intr & WL1251_ACX_INTR_INIT_COMPLETE) | ||||
| 			wl1251_debug(DEBUG_IRQ, | ||||
| 				     "WL1251_ACX_INTR_INIT_COMPLETE"); | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_RX1_DATA) { | ||||
| 		wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); | ||||
| 		wl1251_rx(wl); | ||||
| 	} | ||||
| 		if (--ctr == 0) | ||||
| 			break; | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_TX_RESULT) { | ||||
| 		wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); | ||||
| 		wl1251_tx_complete(wl); | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { | ||||
| 		wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); | ||||
| 		if (intr & WL1251_ACX_INTR_EVENT_A) | ||||
| 			wl1251_event_handle(wl, 0); | ||||
| 		else | ||||
| 			wl1251_event_handle(wl, 1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_INIT_COMPLETE) | ||||
| 		wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); | ||||
| 
 | ||||
| 	wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); | ||||
| 		intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); | ||||
| 	} while (intr); | ||||
| 
 | ||||
| out_sleep: | ||||
| 	wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); | ||||
| 	wl1251_ps_elp_sleep(wl); | ||||
| 
 | ||||
| out: | ||||
| @ -509,6 +519,12 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, | ||||
| 		     conf->type, conf->mac_addr); | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 	if (wl->vif) { | ||||
| 		ret = -EBUSY; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	wl->vif = conf->vif; | ||||
| 
 | ||||
| 	switch (conf->type) { | ||||
| 	case NL80211_IFTYPE_STATION: | ||||
| @ -538,7 +554,12 @@ out: | ||||
| static void wl1251_op_remove_interface(struct ieee80211_hw *hw, | ||||
| 					 struct ieee80211_if_init_conf *conf) | ||||
| { | ||||
| 	struct wl1251 *wl = hw->priv; | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 	wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); | ||||
| 	wl->vif = NULL; | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| } | ||||
| 
 | ||||
| static int wl1251_build_null_data(struct wl1251 *wl) | ||||
| @ -555,7 +576,8 @@ static int wl1251_build_null_data(struct wl1251 *wl) | ||||
| 
 | ||||
| 	memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); | ||||
| 	template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||||
| 						IEEE80211_STYPE_NULLFUNC); | ||||
| 						IEEE80211_STYPE_NULLFUNC | | ||||
| 						IEEE80211_FCTL_TODS); | ||||
| 
 | ||||
| 	return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, | ||||
| 				       sizeof(template)); | ||||
| @ -568,7 +590,10 @@ static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) | ||||
| 
 | ||||
| 	memcpy(template.bssid, wl->bssid, ETH_ALEN); | ||||
| 	memcpy(template.ta, wl->mac_addr, ETH_ALEN); | ||||
| 	template.aid = aid; | ||||
| 
 | ||||
| 	/* aid in PS-Poll has its two MSBs each set to 1 */ | ||||
| 	template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); | ||||
| 
 | ||||
| 	template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); | ||||
| 
 | ||||
| 	return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, | ||||
| @ -1090,8 +1115,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 			wl->beacon_int = bss_conf->beacon_int; | ||||
| 			wl->dtim_period = bss_conf->dtim_period; | ||||
| 
 | ||||
| 			/* FIXME: call join */ | ||||
| 
 | ||||
| 			ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, | ||||
| 							  wl->dtim_period); | ||||
| 			wl->aid = bss_conf->aid; | ||||
| 
 | ||||
| 			ret = wl1251_build_ps_poll(wl, wl->aid); | ||||
| @ -1312,7 +1337,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl) | ||||
| 
 | ||||
| 	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | | ||||
| 		IEEE80211_HW_NOISE_DBM | | ||||
| 		IEEE80211_HW_SUPPORTS_PS; | ||||
| 		IEEE80211_HW_SUPPORTS_PS | | ||||
| 		IEEE80211_HW_BEACON_FILTER; | ||||
| 
 | ||||
| 	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); | ||||
| 	wl->hw->wiphy->max_scan_ssids = 1; | ||||
| @ -1355,6 +1381,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) | ||||
| 	skb_queue_head_init(&wl->tx_queue); | ||||
| 
 | ||||
| 	INIT_WORK(&wl->filter_work, wl1251_filter_work); | ||||
| 	INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); | ||||
| 	wl->channel = WL1251_DEFAULT_CHANNEL; | ||||
| 	wl->scanning = false; | ||||
| 	wl->default_key = 0; | ||||
| @ -1372,6 +1399,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) | ||||
| 	wl->power_level = WL1251_DEFAULT_POWER_LEVEL; | ||||
| 	wl->beacon_int = WL1251_DEFAULT_BEACON_INT; | ||||
| 	wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; | ||||
| 	wl->vif = NULL; | ||||
| 
 | ||||
| 	for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | ||||
| 		wl->tx_frames[i] = NULL; | ||||
| @ -1413,7 +1441,7 @@ int wl1251_free_hw(struct wl1251 *wl) | ||||
| 
 | ||||
| 	kfree(wl->target_mem_map); | ||||
| 	kfree(wl->data_path); | ||||
| 	kfree(wl->fw); | ||||
| 	vfree(wl->fw); | ||||
| 	wl->fw = NULL; | ||||
| 	kfree(wl->nvs); | ||||
| 	wl->nvs = NULL; | ||||
|  | ||||
| @ -28,17 +28,41 @@ | ||||
| 
 | ||||
| #define WL1251_WAKEUP_TIMEOUT 2000 | ||||
| 
 | ||||
| void wl1251_elp_work(struct work_struct *work) | ||||
| { | ||||
| 	struct delayed_work *dwork; | ||||
| 	struct wl1251 *wl; | ||||
| 
 | ||||
| 	dwork = container_of(work, struct delayed_work, work); | ||||
| 	wl = container_of(dwork, struct wl1251, elp_work); | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_PSM, "elp work"); | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 
 | ||||
| 	if (wl->elp || !wl->psm) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_PSM, "chip to elp"); | ||||
| 	wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); | ||||
| 	wl->elp = true; | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| } | ||||
| 
 | ||||
| #define ELP_ENTRY_DELAY  5 | ||||
| 
 | ||||
| /* Routines to toggle sleep mode while in ELP */ | ||||
| void wl1251_ps_elp_sleep(struct wl1251 *wl) | ||||
| { | ||||
| 	if (wl->elp || !wl->psm) | ||||
| 		return; | ||||
| 	unsigned long delay; | ||||
| 
 | ||||
| 	wl1251_debug(DEBUG_PSM, "chip to elp"); | ||||
| 
 | ||||
| 	wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); | ||||
| 
 | ||||
| 	wl->elp = true; | ||||
| 	if (wl->psm) { | ||||
| 		cancel_delayed_work(&wl->elp_work); | ||||
| 		delay = msecs_to_jiffies(ELP_ENTRY_DELAY); | ||||
| 		ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int wl1251_ps_elp_wakeup(struct wl1251 *wl) | ||||
| @ -119,6 +143,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) | ||||
| 	case STATION_POWER_SAVE_MODE: | ||||
| 		wl1251_debug(DEBUG_PSM, "entering psm"); | ||||
| 
 | ||||
| 		/* enable beacon filtering */ | ||||
| 		ret = wl1251_acx_beacon_filter_opt(wl, true); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		ret = wl1251_acx_wake_up_conditions(wl, | ||||
| 						    WAKE_UP_EVENT_DTIM_BITMAP, | ||||
| 						    wl->listen_int); | ||||
| @ -142,6 +171,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		/* disable beacon filtering */ | ||||
| 		ret = wl1251_acx_beacon_filter_opt(wl, false); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		ret = wl1251_acx_wake_up_conditions(wl, | ||||
| 						    WAKE_UP_EVENT_DTIM_BITMAP, | ||||
| 						    wl->listen_int); | ||||
|  | ||||
| @ -31,6 +31,7 @@ | ||||
| int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode); | ||||
| void wl1251_ps_elp_sleep(struct wl1251 *wl); | ||||
| int wl1251_ps_elp_wakeup(struct wl1251 *wl); | ||||
| void wl1251_elp_work(struct work_struct *work); | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __WL1251_PS_H__ */ | ||||
|  | ||||
| @ -370,6 +370,7 @@ enum wl12xx_acx_int_reg { | ||||
|  EEPROM location specified in the EE_ADDR register. | ||||
|  The Wlan hardware hardware clears this bit automatically. | ||||
| *===============================================*/ | ||||
| #define EE_CTL                              (REGISTERS_BASE + 0x2000) | ||||
| #define ACX_EE_CTL_REG                      EE_CTL | ||||
| #define EE_WRITE                            0x00000001ul | ||||
| #define EE_READ                             0x00000002ul | ||||
| @ -380,6 +381,7 @@ enum wl12xx_acx_int_reg { | ||||
|   This register specifies the address | ||||
|   within the EEPROM from/to which to read/write data. | ||||
|   ===============================================*/ | ||||
| #define EE_ADDR                             (REGISTERS_BASE + 0x2008) | ||||
| #define ACX_EE_ADDR_REG                     EE_ADDR | ||||
| 
 | ||||
| /*===============================================
 | ||||
| @ -389,8 +391,12 @@ enum wl12xx_acx_int_reg { | ||||
|   data from the EEPROM or the write data | ||||
|   to be written to the EEPROM. | ||||
|   ===============================================*/ | ||||
| #define EE_DATA                             (REGISTERS_BASE + 0x2004) | ||||
| #define ACX_EE_DATA_REG                     EE_DATA | ||||
| 
 | ||||
| #define EEPROM_ACCESS_TO                    10000   /* timeout counter */ | ||||
| #define START_EEPROM_MGR                    0x00000001 | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   EEPROM Base Address  - 32bit RW | ||||
|   ------------------------------------------ | ||||
|  | ||||
| @ -72,10 +72,6 @@ static void wl1251_rx_status(struct wl1251 *wl, | ||||
| 	} | ||||
| 
 | ||||
| 	status->signal = desc->rssi; | ||||
| 	status->qual = (desc->rssi - WL1251_RX_MIN_RSSI) * 100 / | ||||
| 		(WL1251_RX_MAX_RSSI - WL1251_RX_MIN_RSSI); | ||||
| 	status->qual = min(status->qual, 100); | ||||
| 	status->qual = max(status->qual, 0); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: guessing that snr needs to be divided by two, otherwise | ||||
|  | ||||
| @ -270,6 +270,8 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi) | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	wl->use_eeprom = pdata->use_eeprom; | ||||
| 
 | ||||
| 	ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); | ||||
| 	if (ret < 0) { | ||||
| 		wl1251_error("request_irq() failed: %d", ret); | ||||
|  | ||||
| @ -381,7 +381,7 @@ static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr) | ||||
| 
 | ||||
| static int wl3501_esbq_req_test(struct wl3501_card *this) | ||||
| { | ||||
| 	u8 tmp; | ||||
| 	u8 tmp = 0; | ||||
| 
 | ||||
| 	wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp)); | ||||
| 	return tmp & 0x80; | ||||
|  | ||||
| @ -140,6 +140,19 @@ static void ssb_device_put(struct ssb_device *dev) | ||||
| 		put_device(dev->dev); | ||||
| } | ||||
| 
 | ||||
| static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv) | ||||
| { | ||||
| 	if (drv) | ||||
| 		get_driver(&drv->drv); | ||||
| 	return drv; | ||||
| } | ||||
| 
 | ||||
| static inline void ssb_driver_put(struct ssb_driver *drv) | ||||
| { | ||||
| 	if (drv) | ||||
| 		put_driver(&drv->drv); | ||||
| } | ||||
| 
 | ||||
| static int ssb_device_resume(struct device *dev) | ||||
| { | ||||
| 	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); | ||||
| @ -210,90 +223,81 @@ int ssb_bus_suspend(struct ssb_bus *bus) | ||||
| EXPORT_SYMBOL(ssb_bus_suspend); | ||||
| 
 | ||||
| #ifdef CONFIG_SSB_SPROM | ||||
| int ssb_devices_freeze(struct ssb_bus *bus) | ||||
| /** ssb_devices_freeze - Freeze all devices on the bus.
 | ||||
|  * | ||||
|  * After freezing no device driver will be handling a device | ||||
|  * on this bus anymore. ssb_devices_thaw() must be called after | ||||
|  * a successful freeze to reactivate the devices. | ||||
|  * | ||||
|  * @bus: The bus. | ||||
|  * @ctx: Context structure. Pass this to ssb_devices_thaw(). | ||||
|  */ | ||||
| int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx) | ||||
| { | ||||
| 	struct ssb_device *dev; | ||||
| 	struct ssb_driver *drv; | ||||
| 	int err = 0; | ||||
| 	int i; | ||||
| 	pm_message_t state = PMSG_FREEZE; | ||||
| 	struct ssb_device *sdev; | ||||
| 	struct ssb_driver *sdrv; | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	memset(ctx, 0, sizeof(*ctx)); | ||||
| 	ctx->bus = bus; | ||||
| 	SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen)); | ||||
| 
 | ||||
| 	/* First check that we are capable to freeze all devices. */ | ||||
| 	for (i = 0; i < bus->nr_devices; i++) { | ||||
| 		dev = &(bus->devices[i]); | ||||
| 		if (!dev->dev || | ||||
| 		    !dev->dev->driver || | ||||
| 		    !device_is_registered(dev->dev)) | ||||
| 		sdev = ssb_device_get(&bus->devices[i]); | ||||
| 
 | ||||
| 		if (!sdev->dev || !sdev->dev->driver || | ||||
| 		    !device_is_registered(sdev->dev)) { | ||||
| 			ssb_device_put(sdev); | ||||
| 			continue; | ||||
| 		drv = drv_to_ssb_drv(dev->dev->driver); | ||||
| 		if (!drv) | ||||
| 			continue; | ||||
| 		if (!drv->suspend) { | ||||
| 			/* Nope, can't suspend this one. */ | ||||
| 			return -EOPNOTSUPP; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Now suspend all devices */ | ||||
| 	for (i = 0; i < bus->nr_devices; i++) { | ||||
| 		dev = &(bus->devices[i]); | ||||
| 		if (!dev->dev || | ||||
| 		    !dev->dev->driver || | ||||
| 		    !device_is_registered(dev->dev)) | ||||
| 		sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver)); | ||||
| 		if (!sdrv || SSB_WARN_ON(!sdrv->remove)) { | ||||
| 			ssb_device_put(sdev); | ||||
| 			continue; | ||||
| 		drv = drv_to_ssb_drv(dev->dev->driver); | ||||
| 		if (!drv) | ||||
| 			continue; | ||||
| 		err = drv->suspend(dev, state); | ||||
| 		if (err) { | ||||
| 			ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n", | ||||
| 				   dev_name(dev->dev)); | ||||
| 			goto err_unwind; | ||||
| 		} | ||||
| 		sdrv->remove(sdev); | ||||
| 		ctx->device_frozen[i] = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| err_unwind: | ||||
| 	for (i--; i >= 0; i--) { | ||||
| 		dev = &(bus->devices[i]); | ||||
| 		if (!dev->dev || | ||||
| 		    !dev->dev->driver || | ||||
| 		    !device_is_registered(dev->dev)) | ||||
| 			continue; | ||||
| 		drv = drv_to_ssb_drv(dev->dev->driver); | ||||
| 		if (!drv) | ||||
| 			continue; | ||||
| 		if (drv->resume) | ||||
| 			drv->resume(dev); | ||||
| 	} | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| int ssb_devices_thaw(struct ssb_bus *bus) | ||||
| /** ssb_devices_thaw - Unfreeze all devices on the bus.
 | ||||
|  * | ||||
|  * This will re-attach the device drivers and re-init the devices. | ||||
|  * | ||||
|  * @ctx: The context structure from ssb_devices_freeze() | ||||
|  */ | ||||
| int ssb_devices_thaw(struct ssb_freeze_context *ctx) | ||||
| { | ||||
| 	struct ssb_device *dev; | ||||
| 	struct ssb_driver *drv; | ||||
| 	int err; | ||||
| 	int i; | ||||
| 	struct ssb_bus *bus = ctx->bus; | ||||
| 	struct ssb_device *sdev; | ||||
| 	struct ssb_driver *sdrv; | ||||
| 	unsigned int i; | ||||
| 	int err, result = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < bus->nr_devices; i++) { | ||||
| 		dev = &(bus->devices[i]); | ||||
| 		if (!dev->dev || | ||||
| 		    !dev->dev->driver || | ||||
| 		    !device_is_registered(dev->dev)) | ||||
| 		if (!ctx->device_frozen[i]) | ||||
| 			continue; | ||||
| 		drv = drv_to_ssb_drv(dev->dev->driver); | ||||
| 		if (!drv) | ||||
| 		sdev = &bus->devices[i]; | ||||
| 
 | ||||
| 		if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver)) | ||||
| 			continue; | ||||
| 		if (SSB_WARN_ON(!drv->resume)) | ||||
| 		sdrv = drv_to_ssb_drv(sdev->dev->driver); | ||||
| 		if (SSB_WARN_ON(!sdrv || !sdrv->probe)) | ||||
| 			continue; | ||||
| 		err = drv->resume(dev); | ||||
| 
 | ||||
| 		err = sdrv->probe(sdev, &sdev->id); | ||||
| 		if (err) { | ||||
| 			ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", | ||||
| 				   dev_name(dev->dev)); | ||||
| 				   dev_name(sdev->dev)); | ||||
| 			result = err; | ||||
| 		} | ||||
| 		ssb_driver_put(sdrv); | ||||
| 		ssb_device_put(sdev); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 	return result; | ||||
| } | ||||
| #endif /* CONFIG_SSB_SPROM */ | ||||
| 
 | ||||
|  | ||||
| @ -354,7 +354,7 @@ int ssb_bus_scan(struct ssb_bus *bus, | ||||
| 		dev->bus = bus; | ||||
| 		dev->ops = bus->ops; | ||||
| 
 | ||||
| 		ssb_dprintk(KERN_INFO PFX | ||||
| 		printk(KERN_DEBUG PFX | ||||
| 			    "Core %d found: %s " | ||||
| 			    "(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n", | ||||
| 			    i, ssb_core_name(dev->id.coreid), | ||||
|  | ||||
| @ -13,6 +13,8 @@ | ||||
| 
 | ||||
| #include "ssb_private.h" | ||||
| 
 | ||||
| #include <linux/ctype.h> | ||||
| 
 | ||||
| 
 | ||||
| static const struct ssb_sprom *fallback_sprom; | ||||
| 
 | ||||
| @ -33,17 +35,27 @@ static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, | ||||
| static int hex2sprom(u16 *sprom, const char *dump, size_t len, | ||||
| 		     size_t sprom_size_words) | ||||
| { | ||||
| 	char tmp[5] = { 0 }; | ||||
| 	int cnt = 0; | ||||
| 	char c, tmp[5] = { 0 }; | ||||
| 	int err, cnt = 0; | ||||
| 	unsigned long parsed; | ||||
| 
 | ||||
| 	if (len < sprom_size_words * 2) | ||||
| 	/* Strip whitespace at the end. */ | ||||
| 	while (len) { | ||||
| 		c = dump[len - 1]; | ||||
| 		if (!isspace(c) && c != '\0') | ||||
| 			break; | ||||
| 		len--; | ||||
| 	} | ||||
| 	/* Length must match exactly. */ | ||||
| 	if (len != sprom_size_words * 4) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	while (cnt < sprom_size_words) { | ||||
| 		memcpy(tmp, dump, 4); | ||||
| 		dump += 4; | ||||
| 		parsed = simple_strtoul(tmp, NULL, 16); | ||||
| 		err = strict_strtoul(tmp, 16, &parsed); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 		sprom[cnt++] = swab16((u16)parsed); | ||||
| 	} | ||||
| 
 | ||||
| @ -90,6 +102,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | ||||
| 	u16 *sprom; | ||||
| 	int res = 0, err = -ENOMEM; | ||||
| 	size_t sprom_size_words = bus->sprom_size; | ||||
| 	struct ssb_freeze_context freeze; | ||||
| 
 | ||||
| 	sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||||
| 	if (!sprom) | ||||
| @ -111,18 +124,13 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | ||||
| 	err = -ERESTARTSYS; | ||||
| 	if (mutex_lock_interruptible(&bus->sprom_mutex)) | ||||
| 		goto out_kfree; | ||||
| 	err = ssb_devices_freeze(bus); | ||||
| 	if (err == -EOPNOTSUPP) { | ||||
| 		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " | ||||
| 			   "No suspend support. Is CONFIG_PM enabled?\n"); | ||||
| 		goto out_unlock; | ||||
| 	} | ||||
| 	err = ssb_devices_freeze(bus, &freeze); | ||||
| 	if (err) { | ||||
| 		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); | ||||
| 		goto out_unlock; | ||||
| 	} | ||||
| 	res = sprom_write(bus, sprom); | ||||
| 	err = ssb_devices_thaw(bus); | ||||
| 	err = ssb_devices_thaw(&freeze); | ||||
| 	if (err) | ||||
| 		ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); | ||||
| out_unlock: | ||||
|  | ||||
| @ -176,13 +176,21 @@ extern const struct ssb_sprom *ssb_get_fallback_sprom(void); | ||||
| 
 | ||||
| /* core.c */ | ||||
| extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); | ||||
| extern int ssb_devices_freeze(struct ssb_bus *bus); | ||||
| extern int ssb_devices_thaw(struct ssb_bus *bus); | ||||
| extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); | ||||
| int ssb_for_each_bus_call(unsigned long data, | ||||
| 			  int (*func)(struct ssb_bus *bus, unsigned long data)); | ||||
| extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); | ||||
| 
 | ||||
| struct ssb_freeze_context { | ||||
| 	/* Pointer to the bus */ | ||||
| 	struct ssb_bus *bus; | ||||
| 	/* Boolean list to indicate whether a device is frozen on this bus. */ | ||||
| 	bool device_frozen[SSB_MAX_NR_CORES]; | ||||
| }; | ||||
| extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx); | ||||
| extern int ssb_devices_thaw(struct ssb_freeze_context *ctx); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* b43_pci_bridge.c */ | ||||
| #ifdef CONFIG_SSB_B43_PCI_BRIDGE | ||||
|  | ||||
| @ -115,7 +115,6 @@ | ||||
| #define IEEE80211_MAX_SSID_LEN		32 | ||||
| 
 | ||||
| #define IEEE80211_MAX_MESH_ID_LEN	32 | ||||
| #define IEEE80211_MESH_CONFIG_LEN	7 | ||||
| 
 | ||||
| #define IEEE80211_QOS_CTL_LEN		2 | ||||
| #define IEEE80211_QOS_CTL_TID_MASK	0x000F | ||||
| @ -554,6 +553,21 @@ struct ieee80211_tim_ie { | ||||
| 	u8 virtual_map[1]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ieee80211_meshconf_ie | ||||
|  * | ||||
|  * This structure refers to "Mesh Configuration information element" | ||||
|  */ | ||||
| struct ieee80211_meshconf_ie { | ||||
| 	u8 meshconf_psel; | ||||
| 	u8 meshconf_pmetric; | ||||
| 	u8 meshconf_congest; | ||||
| 	u8 meshconf_synch; | ||||
| 	u8 meshconf_auth; | ||||
| 	u8 meshconf_form; | ||||
| 	u8 meshconf_cap; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ieee80211_rann_ie | ||||
|  * | ||||
|  | ||||
| @ -70,6 +70,7 @@ | ||||
| #define IFF_XMIT_DST_RELEASE 0x400	/* dev_hard_start_xmit() is allowed to | ||||
| 					 * release skb->dst | ||||
| 					 */ | ||||
| #define IFF_DONT_BRIDGE 0x800		/* disallow bridging this ether dev */ | ||||
| 
 | ||||
| #define IF_GET_IFACE	0x0001		/* for querying only */ | ||||
| #define IF_GET_PROTO	0x0002 | ||||
|  | ||||
| @ -35,6 +35,8 @@ | ||||
|  * @RFKILL_TYPE_UWB: switch is on a ultra wideband device. | ||||
|  * @RFKILL_TYPE_WIMAX: switch is on a WiMAX device. | ||||
|  * @RFKILL_TYPE_WWAN: switch is on a wireless WAN device. | ||||
|  * @RFKILL_TYPE_GPS: switch is on a GPS device. | ||||
|  * @RFKILL_TYPE_FM: switch is on a FM radio device. | ||||
|  * @NUM_RFKILL_TYPES: number of defined rfkill types | ||||
|  */ | ||||
| enum rfkill_type { | ||||
| @ -45,6 +47,7 @@ enum rfkill_type { | ||||
| 	RFKILL_TYPE_WIMAX, | ||||
| 	RFKILL_TYPE_WWAN, | ||||
| 	RFKILL_TYPE_GPS, | ||||
| 	RFKILL_TYPE_FM, | ||||
| 	NUM_RFKILL_TYPES, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -26,6 +26,7 @@ | ||||
| 
 | ||||
| struct wl12xx_platform_data { | ||||
| 	void (*set_power)(bool enable); | ||||
| 	bool use_eeprom; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -1108,27 +1108,50 @@ struct cfg80211_ops { | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * struct wiphy - wireless hardware description | ||||
|  * @idx: the wiphy index assigned to this item | ||||
|  * @class_dev: the class device representing /sys/class/ieee80211/<wiphy-name> | ||||
|  * @custom_regulatory: tells us the driver for this device | ||||
|  * enum wiphy_flags - wiphy capability flags | ||||
|  * | ||||
|  * @WIPHY_FLAG_CUSTOM_REGULATORY:  tells us the driver for this device | ||||
|  * 	has its own custom regulatory domain and cannot identify the | ||||
|  * 	ISO / IEC 3166 alpha2 it belongs to. When this is enabled | ||||
|  * 	we will disregard the first regulatory hint (when the | ||||
|  * 	initiator is %REGDOM_SET_BY_CORE). | ||||
|  * @strict_regulatory: tells us the driver for this device will ignore | ||||
|  * 	regulatory domain settings until it gets its own regulatory domain | ||||
|  * 	via its regulatory_hint(). After its gets its own regulatory domain | ||||
|  * 	it will only allow further regulatory domain settings to further | ||||
|  * 	enhance compliance. For example if channel 13 and 14 are disabled | ||||
|  * 	by this regulatory domain no user regulatory domain can enable these | ||||
|  * 	channels at a later time. This can be used for devices which do not | ||||
|  * 	have calibration information gauranteed for frequencies or settings | ||||
|  * 	outside of its regulatory domain. | ||||
|  * @disable_beacon_hints: enable this if your driver needs to ensure that | ||||
|  *	passive scan flags and beaconing flags may not be lifted by cfg80211 | ||||
|  *	due to regulatory beacon hints. For more information on beacon | ||||
|  * @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will | ||||
|  *	ignore regulatory domain settings until it gets its own regulatory | ||||
|  *	domain via its regulatory_hint(). After its gets its own regulatory | ||||
|  *	domain it will only allow further regulatory domain settings to | ||||
|  *	further enhance compliance. For example if channel 13 and 14 are | ||||
|  *	disabled by this regulatory domain no user regulatory domain can | ||||
|  *	enable these channels at a later time. This can be used for devices | ||||
|  *	which do not have calibration information gauranteed for frequencies | ||||
|  *	or settings outside of its regulatory domain. | ||||
|  * @WIPHY_FLAG_DISABLE_BEACON_HINTS: enable this if your driver needs to ensure | ||||
|  *	that passive scan flags and beaconing flags may not be lifted by | ||||
|  *	cfg80211 due to regulatory beacon hints. For more information on beacon | ||||
|  *	hints read the documenation for regulatory_hint_found_beacon() | ||||
|  * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this | ||||
|  *	wiphy at all | ||||
|  * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled | ||||
|  *	by default -- this flag will be set depending on the kernel's default | ||||
|  *	on wiphy_new(), but can be changed by the driver if it has a good | ||||
|  *	reason to override the default | ||||
|  * @WIPHY_FLAG_4ADDR_AP: supports 4addr mode even on AP (with a single station | ||||
|  *	on a VLAN interface) | ||||
|  * @WIPHY_FLAG_4ADDR_STATION: supports 4addr mode even as a station | ||||
|  */ | ||||
| enum wiphy_flags { | ||||
| 	WIPHY_FLAG_CUSTOM_REGULATORY	= BIT(0), | ||||
| 	WIPHY_FLAG_STRICT_REGULATORY	= BIT(1), | ||||
| 	WIPHY_FLAG_DISABLE_BEACON_HINTS	= BIT(2), | ||||
| 	WIPHY_FLAG_NETNS_OK		= BIT(3), | ||||
| 	WIPHY_FLAG_PS_ON_BY_DEFAULT	= BIT(4), | ||||
| 	WIPHY_FLAG_4ADDR_AP		= BIT(5), | ||||
| 	WIPHY_FLAG_4ADDR_STATION	= BIT(6), | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct wiphy - wireless hardware description | ||||
|  * @idx: the wiphy index assigned to this item | ||||
|  * @class_dev: the class device representing /sys/class/ieee80211/<wiphy-name> | ||||
|  * @reg_notifier: the driver's regulatory notification callback | ||||
|  * @regd: the driver's regulatory domain, if one was requested via | ||||
|  * 	the regulatory_hint() API. This can be used by the driver | ||||
| @ -1143,11 +1166,6 @@ struct cfg80211_ops { | ||||
|  *	-1 = fragmentation disabled, only odd values >= 256 used | ||||
|  * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled | ||||
|  * @net: the network namespace this wiphy currently lives in | ||||
|  * @netnsok: if set to false, do not allow changing the netns of this | ||||
|  *	wiphy at all | ||||
|  * @ps_default: default for powersave, will be set depending on the | ||||
|  *	kernel's default on wiphy_new(), but can be changed by the | ||||
|  *	driver if it has a good reason to override the default | ||||
|  */ | ||||
| struct wiphy { | ||||
| 	/* assign these fields before you register the wiphy */ | ||||
| @ -1158,12 +1176,7 @@ struct wiphy { | ||||
| 	/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ | ||||
| 	u16 interface_modes; | ||||
| 
 | ||||
| 	bool custom_regulatory; | ||||
| 	bool strict_regulatory; | ||||
| 	bool disable_beacon_hints; | ||||
| 
 | ||||
| 	bool netnsok; | ||||
| 	bool ps_default; | ||||
| 	u32 flags; | ||||
| 
 | ||||
| 	enum cfg80211_signal_type signal_type; | ||||
| 
 | ||||
| @ -1358,6 +1371,10 @@ struct cfg80211_cached_keys; | ||||
|  * @ssid_len: (private) Used by the internal configuration code | ||||
|  * @wext: (private) Used by the internal wireless extensions compat code | ||||
|  * @wext_bssid: (private) Used by the internal wireless extensions compat code | ||||
|  * @use_4addr: indicates 4addr mode is used on this interface, must be | ||||
|  *	set by driver (if supported) on add_interface BEFORE registering the | ||||
|  *	netdev and may otherwise be used by driver read-only, will be update | ||||
|  *	by cfg80211 on change_interface | ||||
|  */ | ||||
| struct wireless_dev { | ||||
| 	struct wiphy *wiphy; | ||||
| @ -1371,6 +1388,8 @@ struct wireless_dev { | ||||
| 
 | ||||
| 	struct work_struct cleanup_work; | ||||
| 
 | ||||
| 	bool use_4addr; | ||||
| 
 | ||||
| 	/* currently used for IBSS and SME - might be rearranged later */ | ||||
| 	u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||||
| 	u8 ssid_len; | ||||
| @ -1819,6 +1838,18 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); | ||||
|  */ | ||||
| void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr); | ||||
| 
 | ||||
| /**
 | ||||
|  * __cfg80211_auth_canceled - notify cfg80211 that authentication was canceled | ||||
|  * @dev: network device | ||||
|  * @addr: The MAC address of the device with which the authentication timed out | ||||
|  * | ||||
|  * When a pending authentication had no action yet, the driver may decide | ||||
|  * to not send a deauth frame, but in that case must calls this function | ||||
|  * to tell cfg80211 about this decision. It is only valid to call this | ||||
|  * function within the deauth() callback. | ||||
|  */ | ||||
| void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr); | ||||
| 
 | ||||
| /**
 | ||||
|  * cfg80211_send_rx_assoc - notification of processed association | ||||
|  * @dev: network device | ||||
|  | ||||
| @ -219,7 +219,7 @@ struct ieee80211_bss_conf { | ||||
|  * | ||||
|  * These flags are used with the @flags member of &ieee80211_tx_info. | ||||
|  * | ||||
|  * @IEEE80211_TX_CTL_REQ_TX_STATUS: request TX status callback for this frame. | ||||
|  * @IEEE80211_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame. | ||||
|  * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence | ||||
|  *	number to this frame, taking care of not overwriting the fragment | ||||
|  *	number and increasing the sequence number only when the | ||||
| @ -390,10 +390,12 @@ struct ieee80211_tx_rate { | ||||
|  * @control: union for control data | ||||
|  * @status: union for status data | ||||
|  * @driver_data: array of driver_data pointers | ||||
|  * @ampdu_ack_len: number of aggregated frames. | ||||
|  * @ampdu_ack_len: number of acked aggregated frames. | ||||
|  * 	relevant only if IEEE80211_TX_STATUS_AMPDU was set. | ||||
|  * @ampdu_ack_map: block ack bit map for the aggregation. | ||||
|  * 	relevant only if IEEE80211_TX_STATUS_AMPDU was set. | ||||
|  * @ampdu_len: number of aggregated frames. | ||||
|  * 	relevant only if IEEE80211_TX_STATUS_AMPDU was set. | ||||
|  * @ack_signal: signal strength of the ACK frame | ||||
|  */ | ||||
| struct ieee80211_tx_info { | ||||
| @ -428,7 +430,8 @@ struct ieee80211_tx_info { | ||||
| 			u8 ampdu_ack_len; | ||||
| 			u64 ampdu_ack_map; | ||||
| 			int ack_signal; | ||||
| 			/* 8 bytes free */ | ||||
| 			u8 ampdu_len; | ||||
| 			/* 7 bytes free */ | ||||
| 		} status; | ||||
| 		struct { | ||||
| 			struct ieee80211_tx_rate driver_rates[ | ||||
| @ -852,6 +855,19 @@ enum ieee80211_tkip_key_type { | ||||
|  * any particular flags. There are some exceptions to this rule, | ||||
|  * however, so you are advised to review these flags carefully. | ||||
|  * | ||||
|  * @IEEE80211_HW_HAS_RATE_CONTROL: | ||||
|  *	The hardware or firmware includes rate control, and cannot be | ||||
|  *	controlled by the stack. As such, no rate control algorithm | ||||
|  *	should be instantiated, and the TX rate reported to userspace | ||||
|  *	will be taken from the TX status instead of the rate control | ||||
|  *	algorithm. | ||||
|  *	Note that this requires that the driver implement a number of | ||||
|  *	callbacks so it has the correct information, it needs to have | ||||
|  *	the @set_rts_threshold callback and must look at the BSS config | ||||
|  *	@use_cts_prot for G/N protection, @use_short_slot for slot | ||||
|  *	timing in 2.4 GHz and @use_short_preamble for preambles for | ||||
|  *	CCK frames. | ||||
|  * | ||||
|  * @IEEE80211_HW_RX_INCLUDES_FCS: | ||||
|  *	Indicates that received frames passed to the stack include | ||||
|  *	the FCS at the end. | ||||
| @ -910,6 +926,7 @@ enum ieee80211_tkip_key_type { | ||||
|  *	avoid waking up cpu. | ||||
|  */ | ||||
| enum ieee80211_hw_flags { | ||||
| 	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0, | ||||
| 	IEEE80211_HW_RX_INCLUDES_FCS			= 1<<1, | ||||
| 	IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING	= 1<<2, | ||||
| 	IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE		= 1<<3, | ||||
| @ -1505,6 +1522,7 @@ struct ieee80211_ops { | ||||
| 	void (*reset_tsf)(struct ieee80211_hw *hw); | ||||
| 	int (*tx_last_beacon)(struct ieee80211_hw *hw); | ||||
| 	int (*ampdu_action)(struct ieee80211_hw *hw, | ||||
| 			    struct ieee80211_vif *vif, | ||||
| 			    enum ieee80211_ampdu_mlme_action action, | ||||
| 			    struct ieee80211_sta *sta, u16 tid, u16 *ssn); | ||||
| 
 | ||||
| @ -2026,8 +2044,7 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, | ||||
| 
 | ||||
| /**
 | ||||
|  * ieee80211_start_tx_ba_session - Start a tx Block Ack session. | ||||
|  * @hw: pointer as obtained from ieee80211_alloc_hw(). | ||||
|  * @ra: receiver address of the BA session recipient | ||||
|  * @sta: the station for which to start a BA session | ||||
|  * @tid: the TID to BA on. | ||||
|  * | ||||
|  * Return: success if addBA request was sent, failure otherwise | ||||
| @ -2036,22 +2053,22 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, | ||||
|  * the need to start aggregation on a certain RA/TID, the session level | ||||
|  * will be managed by the mac80211. | ||||
|  */ | ||||
| int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid); | ||||
| int ieee80211_start_tx_ba_session(struct ieee80211_sta *sta, u16 tid); | ||||
| 
 | ||||
| /**
 | ||||
|  * ieee80211_start_tx_ba_cb - low level driver ready to aggregate. | ||||
|  * @hw: pointer as obtained from ieee80211_alloc_hw(). | ||||
|  * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf | ||||
|  * @ra: receiver address of the BA session recipient. | ||||
|  * @tid: the TID to BA on. | ||||
|  * | ||||
|  * This function must be called by low level driver once it has | ||||
|  * finished with preparations for the BA session. | ||||
|  */ | ||||
| void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid); | ||||
| void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid); | ||||
| 
 | ||||
| /**
 | ||||
|  * ieee80211_start_tx_ba_cb_irqsafe - low level driver ready to aggregate. | ||||
|  * @hw: pointer as obtained from ieee80211_alloc_hw(). | ||||
|  * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf | ||||
|  * @ra: receiver address of the BA session recipient. | ||||
|  * @tid: the TID to BA on. | ||||
|  * | ||||
| @ -2059,13 +2076,12 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid); | ||||
|  * finished with preparations for the BA session. | ||||
|  * This version of the function is IRQ-safe. | ||||
|  */ | ||||
| void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra, | ||||
| void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, | ||||
| 				      u16 tid); | ||||
| 
 | ||||
| /**
 | ||||
|  * ieee80211_stop_tx_ba_session - Stop a Block Ack session. | ||||
|  * @hw: pointer as obtained from ieee80211_alloc_hw(). | ||||
|  * @ra: receiver address of the BA session recipient | ||||
|  * @sta: the station whose BA session to stop | ||||
|  * @tid: the TID to stop BA. | ||||
|  * @initiator: if indicates initiator DELBA frame will be sent. | ||||
|  * | ||||
| @ -2075,24 +2091,23 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra, | ||||
|  * the need to stop aggregation on a certain RA/TID, the session level | ||||
|  * will be managed by the mac80211. | ||||
|  */ | ||||
| int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, | ||||
| 				 u8 *ra, u16 tid, | ||||
| int ieee80211_stop_tx_ba_session(struct ieee80211_sta *sta, u16 tid, | ||||
| 				 enum ieee80211_back_parties initiator); | ||||
| 
 | ||||
| /**
 | ||||
|  * ieee80211_stop_tx_ba_cb - low level driver ready to stop aggregate. | ||||
|  * @hw: pointer as obtained from ieee80211_alloc_hw(). | ||||
|  * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf | ||||
|  * @ra: receiver address of the BA session recipient. | ||||
|  * @tid: the desired TID to BA on. | ||||
|  * | ||||
|  * This function must be called by low level driver once it has | ||||
|  * finished with preparations for the BA session tear down. | ||||
|  */ | ||||
| void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid); | ||||
| void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); | ||||
| 
 | ||||
| /**
 | ||||
|  * ieee80211_stop_tx_ba_cb_irqsafe - low level driver ready to stop aggregate. | ||||
|  * @hw: pointer as obtained from ieee80211_alloc_hw(). | ||||
|  * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf | ||||
|  * @ra: receiver address of the BA session recipient. | ||||
|  * @tid: the desired TID to BA on. | ||||
|  * | ||||
| @ -2100,7 +2115,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid); | ||||
|  * finished with preparations for the BA session tear down. | ||||
|  * This version of the function is IRQ-safe. | ||||
|  */ | ||||
| void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra, | ||||
| void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, | ||||
| 				     u16 tid); | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
| @ -390,6 +390,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | ||||
| 	if (dev->br_port != NULL) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	/* No bridging devices that dislike that (e.g. wireless) */ | ||||
| 	if (dev->priv_flags & IFF_DONT_BRIDGE) | ||||
| 		return -EOPNOTSUPP; | ||||
| 
 | ||||
| 	p = new_nbp(br, dev); | ||||
| 	if (IS_ERR(p)) | ||||
| 		return PTR_ERR(p); | ||||
|  | ||||
| @ -2,7 +2,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o | ||||
| 
 | ||||
| # mac80211 objects
 | ||||
| mac80211-y := \
 | ||||
| 	main.o \
 | ||||
| 	main.o status.o \
 | ||||
| 	sta_info.o \
 | ||||
| 	wep.o \
 | ||||
| 	wpa.o \
 | ||||
|  | ||||
| @ -41,7 +41,8 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | ||||
| 	       sta->sta.addr, tid); | ||||
| #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||||
| 
 | ||||
| 	if (drv_ampdu_action(local, IEEE80211_AMPDU_RX_STOP, | ||||
| 	if (drv_ampdu_action(local, &sta->sdata->vif, | ||||
| 			     IEEE80211_AMPDU_RX_STOP, | ||||
| 			     &sta->sta, tid, NULL)) | ||||
| 		printk(KERN_DEBUG "HW problem - can not stop rx " | ||||
| 				"aggregation for tid %d\n", tid); | ||||
| @ -170,7 +171,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d | ||||
| 	mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout); | ||||
| 	mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); | ||||
| 
 | ||||
| 	ieee80211_tx_skb(sdata, skb, 1); | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_process_addba_request(struct ieee80211_local *local, | ||||
| @ -284,7 +285,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = drv_ampdu_action(local, IEEE80211_AMPDU_RX_START, | ||||
| 	ret = drv_ampdu_action(local, &sta->sdata->vif, | ||||
| 			       IEEE80211_AMPDU_RX_START, | ||||
| 			       &sta->sta, tid, &start_seq_num); | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| 	printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret); | ||||
|  | ||||
| @ -91,7 +91,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, | ||||
| 	mgmt->u.action.u.addba_req.start_seq_num = | ||||
| 					cpu_to_le16(start_seq_num << 4); | ||||
| 
 | ||||
| 	ieee80211_tx_skb(sdata, skb, 1); | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn) | ||||
| @ -120,7 +120,8 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1 | ||||
| 	bar->control = cpu_to_le16(bar_control); | ||||
| 	bar->start_seq_num = cpu_to_le16(ssn); | ||||
| 
 | ||||
| 	ieee80211_tx_skb(sdata, skb, 0); | ||||
| 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| } | ||||
| 
 | ||||
| static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | ||||
| @ -138,7 +139,8 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | ||||
| 	*state = HT_AGG_STATE_REQ_STOP_BA_MSK | | ||||
| 		(initiator << HT_AGG_STATE_INITIATOR_SHIFT); | ||||
| 
 | ||||
| 	ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_STOP, | ||||
| 	ret = drv_ampdu_action(local, &sta->sdata->vif, | ||||
| 			       IEEE80211_AMPDU_TX_STOP, | ||||
| 			       &sta->sta, tid, NULL); | ||||
| 
 | ||||
| 	/* HW shall not deny going back to legacy */ | ||||
| @ -196,11 +198,11 @@ static inline int ieee80211_ac_from_tid(int tid) | ||||
| 	return ieee802_1d_to_ac[tid & 7]; | ||||
| } | ||||
| 
 | ||||
| int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct sta_info *sta; | ||||
| 	struct ieee80211_sub_if_data *sdata; | ||||
| 	struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | ||||
| 	struct ieee80211_sub_if_data *sdata = sta->sdata; | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	u8 *state; | ||||
| 	int ret = 0; | ||||
| 	u16 start_seq_num; | ||||
| @ -208,52 +210,37 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| 	if (WARN_ON(!local->ops->ampdu_action)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) | ||||
| 	if ((tid >= STA_TID_NUM) || | ||||
| 	    !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| 	printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n", | ||||
| 	       ra, tid); | ||||
| 	       pubsta->addr, tid); | ||||
| #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 
 | ||||
| 	sta = sta_info_get(local, ra); | ||||
| 	if (!sta) { | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| 		printk(KERN_DEBUG "Could not find the station\n"); | ||||
| #endif | ||||
| 		ret = -ENOENT; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The aggregation code is not prepared to handle | ||||
| 	 * anything but STA/AP due to the BSSID handling. | ||||
| 	 * IBSS could work in the code but isn't supported | ||||
| 	 * by drivers or the standard. | ||||
| 	 */ | ||||
| 	if (sta->sdata->vif.type != NL80211_IFTYPE_STATION && | ||||
| 	    sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | ||||
| 	    sta->sdata->vif.type != NL80211_IFTYPE_AP) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 	if (sdata->vif.type != NL80211_IFTYPE_STATION && | ||||
| 	    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | ||||
| 	    sdata->vif.type != NL80211_IFTYPE_AP) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| 		printk(KERN_DEBUG "Suspend in progress. " | ||||
| 		       "Denying BA session request\n"); | ||||
| #endif | ||||
| 		ret = -EINVAL; | ||||
| 		goto unlock; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock_bh(&sta->lock); | ||||
| 	spin_lock(&local->ampdu_lock); | ||||
| 
 | ||||
| 	sdata = sta->sdata; | ||||
| 
 | ||||
| 	/* we have tried too many times, receiver does not want A-MPDU */ | ||||
| 	if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { | ||||
| 		ret = -EBUSY; | ||||
| @ -310,8 +297,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| 
 | ||||
| 	start_seq_num = sta->tid_seq[tid]; | ||||
| 
 | ||||
| 	ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_START, | ||||
| 			       &sta->sta, tid, &start_seq_num); | ||||
| 	ret = drv_ampdu_action(local, &sdata->vif, | ||||
| 			       IEEE80211_AMPDU_TX_START, | ||||
| 			       pubsta, tid, &start_seq_num); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| @ -336,7 +324,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| 			sta->ampdu_mlme.dialog_token_allocator; | ||||
| 	sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; | ||||
| 
 | ||||
| 	ieee80211_send_addba_request(sta->sdata, ra, tid, | ||||
| 	ieee80211_send_addba_request(sdata, pubsta->addr, tid, | ||||
| 			 sta->ampdu_mlme.tid_tx[tid]->dialog_token, | ||||
| 			 sta->ampdu_mlme.tid_tx[tid]->ssn, | ||||
| 			 0x40, 5000); | ||||
| @ -348,7 +336,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| 	printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | ||||
| #endif | ||||
| 	goto unlock; | ||||
| 	return 0; | ||||
| 
 | ||||
|  err_free: | ||||
| 	kfree(sta->ampdu_mlme.tid_tx[tid]); | ||||
| @ -360,8 +348,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
|  err_unlock_sta: | ||||
| 	spin_unlock(&local->ampdu_lock); | ||||
| 	spin_unlock_bh(&sta->lock); | ||||
|  unlock: | ||||
| 	rcu_read_unlock(); | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | ||||
| @ -428,13 +414,15 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | ||||
| 	ieee80211_agg_splice_finish(local, sta, tid); | ||||
| 	spin_unlock(&local->ampdu_lock); | ||||
| 
 | ||||
| 	drv_ampdu_action(local, IEEE80211_AMPDU_TX_OPERATIONAL, | ||||
| 	drv_ampdu_action(local, &sta->sdata->vif, | ||||
| 			 IEEE80211_AMPDU_TX_OPERATIONAL, | ||||
| 			 &sta->sta, tid, NULL); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	struct sta_info *sta; | ||||
| 	u8 *state; | ||||
| 
 | ||||
| @ -483,10 +471,11 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| } | ||||
| EXPORT_SYMBOL(ieee80211_start_tx_ba_cb); | ||||
| 
 | ||||
| void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, | ||||
| void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, | ||||
| 				      const u8 *ra, u16 tid) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	struct ieee80211_ra_tid *ra_tid; | ||||
| 	struct sk_buff *skb = dev_alloc_skb(0); | ||||
| 
 | ||||
| @ -501,6 +490,7 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, | ||||
| 	ra_tid = (struct ieee80211_ra_tid *) &skb->cb; | ||||
| 	memcpy(&ra_tid->ra, ra, ETH_ALEN); | ||||
| 	ra_tid->tid = tid; | ||||
| 	ra_tid->vif = vif; | ||||
| 
 | ||||
| 	skb->pkt_type = IEEE80211_ADDBA_MSG; | ||||
| 	skb_queue_tail(&local->skb_queue, skb); | ||||
| @ -535,13 +525,12 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, | ||||
| 				 u8 *ra, u16 tid, | ||||
| int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, | ||||
| 				 enum ieee80211_back_parties initiator) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct sta_info *sta; | ||||
| 	int ret = 0; | ||||
| 	struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | ||||
| 	struct ieee80211_sub_if_data *sdata = sta->sdata; | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 
 | ||||
| 	if (WARN_ON(!local->ops->ampdu_action)) | ||||
| 		return -EINVAL; | ||||
| @ -549,22 +538,14 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, | ||||
| 	if (tid >= STA_TID_NUM) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	sta = sta_info_get(local, ra); | ||||
| 	if (!sta) { | ||||
| 		rcu_read_unlock(); | ||||
| 		return -ENOENT; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = __ieee80211_stop_tx_ba_session(sta, tid, initiator); | ||||
| 	rcu_read_unlock(); | ||||
| 	return ret; | ||||
| 	return __ieee80211_stop_tx_ba_session(sta, tid, initiator); | ||||
| } | ||||
| EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); | ||||
| 
 | ||||
| void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | ||||
| void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	struct sta_info *sta; | ||||
| 	u8 *state; | ||||
| 
 | ||||
| @ -627,10 +608,11 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | ||||
| } | ||||
| EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb); | ||||
| 
 | ||||
| void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, | ||||
| void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, | ||||
| 				     const u8 *ra, u16 tid) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	struct ieee80211_ra_tid *ra_tid; | ||||
| 	struct sk_buff *skb = dev_alloc_skb(0); | ||||
| 
 | ||||
| @ -645,6 +627,7 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, | ||||
| 	ra_tid = (struct ieee80211_ra_tid *) &skb->cb; | ||||
| 	memcpy(&ra_tid->ra, ra, ETH_ALEN); | ||||
| 	ra_tid->tid = tid; | ||||
| 	ra_tid->vif = vif; | ||||
| 
 | ||||
| 	skb->pkt_type = IEEE80211_DELBA_MSG; | ||||
| 	skb_queue_tail(&local->skb_queue, skb); | ||||
|  | ||||
| @ -42,15 +42,6 @@ static bool nl80211_params_check(enum nl80211_iftype type, | ||||
| 	if (!nl80211_type_check(type)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (params->use_4addr > 0) { | ||||
| 		switch(type) { | ||||
| 		case NL80211_IFTYPE_AP_VLAN: | ||||
| 		case NL80211_IFTYPE_STATION: | ||||
| 			break; | ||||
| 		default: | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| @ -107,12 +98,16 @@ static int ieee80211_change_iface(struct wiphy *wiphy, | ||||
| 					    params->mesh_id_len, | ||||
| 					    params->mesh_id); | ||||
| 
 | ||||
| 	if (params->use_4addr >= 0) | ||||
| 		sdata->use_4addr = !!params->use_4addr; | ||||
| 
 | ||||
| 	if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (type == NL80211_IFTYPE_AP_VLAN && | ||||
| 	    params && params->use_4addr == 0) | ||||
| 		rcu_assign_pointer(sdata->u.vlan.sta, NULL); | ||||
| 	else if (type == NL80211_IFTYPE_STATION && | ||||
| 		 params && params->use_4addr >= 0) | ||||
| 		sdata->u.mgd.use_4addr = params->use_4addr; | ||||
| 
 | ||||
| 	sdata->u.mntr_flags = *flags; | ||||
| 	return 0; | ||||
| } | ||||
| @ -398,13 +393,13 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | ||||
| static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, | ||||
| 				 int idx, u8 *mac, struct station_info *sinfo) | ||||
| { | ||||
| 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||||
| 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||||
| 	struct sta_info *sta; | ||||
| 	int ret = -ENOENT; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 
 | ||||
| 	sta = sta_info_get_by_idx(local, idx, dev); | ||||
| 	sta = sta_info_get_by_idx(sdata, idx); | ||||
| 	if (sta) { | ||||
| 		ret = 0; | ||||
| 		memcpy(mac, sta->sta.addr, ETH_ALEN); | ||||
| @ -827,9 +822,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		if (vlansdata->use_4addr) { | ||||
| 			if (vlansdata->u.vlan.sta) | ||||
| 		if (params->vlan->ieee80211_ptr->use_4addr) { | ||||
| 			if (vlansdata->u.vlan.sta) { | ||||
| 				rcu_read_unlock(); | ||||
| 				return -EBUSY; | ||||
| 			} | ||||
| 
 | ||||
| 			rcu_assign_pointer(vlansdata->u.vlan.sta, sta); | ||||
| 		} | ||||
|  | ||||
| @ -52,7 +52,7 @@ DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d", | ||||
| DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x", | ||||
| 		      local->wep_iv & 0xffffff); | ||||
| DEBUGFS_READONLY_FILE(rate_ctrl_alg, 100, "%s", | ||||
| 		      local->rate_ctrl ? local->rate_ctrl->ops->name : "<unset>"); | ||||
| 	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); | ||||
| 
 | ||||
| static ssize_t tsf_read(struct file *file, char __user *user_buf, | ||||
| 			     size_t count, loff_t *ppos) | ||||
|  | ||||
| @ -157,6 +157,34 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, | ||||
| } | ||||
| STA_OPS(agg_status); | ||||
| 
 | ||||
| static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, | ||||
| 				size_t count, loff_t *ppos) | ||||
| { | ||||
| 	char buf[200], *p = buf; | ||||
| 	int i; | ||||
| 	struct sta_info *sta = file->private_data; | ||||
| 	struct ieee80211_sta_ht_cap *htc = &sta->sta.ht_cap; | ||||
| 
 | ||||
| 	p += scnprintf(p, sizeof(buf) + buf - p, "ht %ssupported\n", | ||||
| 			htc->ht_supported ? "" : "not "); | ||||
| 	if (htc->ht_supported) { | ||||
| 		p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.2x\n", htc->cap); | ||||
| 		p += scnprintf(p, sizeof(buf)+buf-p, "ampdu factor/density: %d/%d\n", | ||||
| 				htc->ampdu_factor, htc->ampdu_density); | ||||
| 		p += scnprintf(p, sizeof(buf)+buf-p, "MCS mask:"); | ||||
| 		for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) | ||||
| 			p += scnprintf(p, sizeof(buf)+buf-p, " %.2x", | ||||
| 					htc->mcs.rx_mask[i]); | ||||
| 		p += scnprintf(p, sizeof(buf)+buf-p, "\nMCS rx highest: %d\n", | ||||
| 				le16_to_cpu(htc->mcs.rx_highest)); | ||||
| 		p += scnprintf(p, sizeof(buf)+buf-p, "MCS tx params: %x\n", | ||||
| 				htc->mcs.tx_params); | ||||
| 	} | ||||
| 
 | ||||
| 	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); | ||||
| } | ||||
| STA_OPS(ht_capa); | ||||
| 
 | ||||
| #define DEBUGFS_ADD(name) \ | ||||
| 	debugfs_create_file(#name, 0400, \ | ||||
| 		sta->debugfs.dir, sta, &sta_ ##name## _ops); | ||||
| @ -207,6 +235,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) | ||||
| 	DEBUGFS_ADD(last_signal); | ||||
| 	DEBUGFS_ADD(last_noise); | ||||
| 	DEBUGFS_ADD(wep_weak_iv_count); | ||||
| 	DEBUGFS_ADD(ht_capa); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_sta_debugfs_remove(struct sta_info *sta) | ||||
|  | ||||
| @ -239,15 +239,16 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local) | ||||
| } | ||||
| 
 | ||||
| static inline int drv_ampdu_action(struct ieee80211_local *local, | ||||
| 				   struct ieee80211_vif *vif, | ||||
| 				   enum ieee80211_ampdu_mlme_action action, | ||||
| 				   struct ieee80211_sta *sta, u16 tid, | ||||
| 				   u16 *ssn) | ||||
| { | ||||
| 	int ret = -EOPNOTSUPP; | ||||
| 	if (local->ops->ampdu_action) | ||||
| 		ret = local->ops->ampdu_action(&local->hw, action, | ||||
| 		ret = local->ops->ampdu_action(&local->hw, vif, action, | ||||
| 					       sta, tid, ssn); | ||||
| 	trace_drv_ampdu_action(local, action, sta, tid, ssn, ret); | ||||
| 	trace_drv_ampdu_action(local, vif, action, sta, tid, ssn, ret); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -634,11 +634,12 @@ TRACE_EVENT(drv_tx_last_beacon, | ||||
| 
 | ||||
| TRACE_EVENT(drv_ampdu_action, | ||||
| 	TP_PROTO(struct ieee80211_local *local, | ||||
| 		 struct ieee80211_vif *vif, | ||||
| 		 enum ieee80211_ampdu_mlme_action action, | ||||
| 		 struct ieee80211_sta *sta, u16 tid, | ||||
| 		 u16 *ssn, int ret), | ||||
| 
 | ||||
| 	TP_ARGS(local, action, sta, tid, ssn, ret), | ||||
| 	TP_ARGS(local, vif, action, sta, tid, ssn, ret), | ||||
| 
 | ||||
| 	TP_STRUCT__entry( | ||||
| 		LOCAL_ENTRY | ||||
| @ -647,10 +648,12 @@ TRACE_EVENT(drv_ampdu_action, | ||||
| 		__field(u16, tid) | ||||
| 		__field(u16, ssn) | ||||
| 		__field(int, ret) | ||||
| 		VIF_ENTRY | ||||
| 	), | ||||
| 
 | ||||
| 	TP_fast_assign( | ||||
| 		LOCAL_ASSIGN; | ||||
| 		VIF_ASSIGN; | ||||
| 		STA_ASSIGN; | ||||
| 		__entry->ret = ret; | ||||
| 		__entry->action = action; | ||||
| @ -659,8 +662,8 @@ TRACE_EVENT(drv_ampdu_action, | ||||
| 	), | ||||
| 
 | ||||
| 	TP_printk( | ||||
| 		LOCAL_PR_FMT  STA_PR_FMT " action:%d tid:%d ret:%d", | ||||
| 		LOCAL_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret | ||||
| 		LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d ret:%d", | ||||
| 		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret | ||||
| 	) | ||||
| ); | ||||
| #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ | ||||
|  | ||||
| @ -134,14 +134,13 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | ||||
| 	mgmt->u.action.u.delba.params = cpu_to_le16(params); | ||||
| 	mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); | ||||
| 
 | ||||
| 	ieee80211_tx_skb(sdata, skb, 1); | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | ||||
| 			     struct sta_info *sta, | ||||
| 			     struct ieee80211_mgmt *mgmt, size_t len) | ||||
| { | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	u16 tid, params; | ||||
| 	u16 initiator; | ||||
| 
 | ||||
| @ -164,7 +163,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | ||||
| 		sta->ampdu_mlme.tid_state_tx[tid] = | ||||
| 				HT_AGG_STATE_OPERATIONAL; | ||||
| 		spin_unlock_bh(&sta->lock); | ||||
| 		ieee80211_stop_tx_ba_session(&local->hw, sta->sta.addr, tid, | ||||
| 		ieee80211_stop_tx_ba_session(&sta->sta, tid, | ||||
| 					     WLAN_BACK_RECIPIENT); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -659,7 +659,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, | ||||
| 	printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n", | ||||
| 	       sdata->dev->name, resp->da); | ||||
| #endif /* CONFIG_MAC80211_IBSS_DEBUG */ | ||||
| 	ieee80211_tx_skb(sdata, skb, 0); | ||||
| 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| } | ||||
| 
 | ||||
| static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
| #include <linux/types.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <net/ieee80211_radiotap.h> | ||||
| #include <net/cfg80211.h> | ||||
| #include <net/mac80211.h> | ||||
| #include "key.h" | ||||
| @ -167,13 +168,10 @@ typedef unsigned __bitwise__ ieee80211_rx_result; | ||||
| 
 | ||||
| struct ieee80211_rx_data { | ||||
| 	struct sk_buff *skb; | ||||
| 	struct net_device *dev; | ||||
| 	struct ieee80211_local *local; | ||||
| 	struct ieee80211_sub_if_data *sdata; | ||||
| 	struct sta_info *sta; | ||||
| 	struct ieee80211_key *key; | ||||
| 	struct ieee80211_rx_status *status; | ||||
| 	struct ieee80211_rate *rate; | ||||
| 
 | ||||
| 	unsigned int flags; | ||||
| 	int queue; | ||||
| @ -314,6 +312,8 @@ struct ieee80211_if_managed { | ||||
| 	} mfp; /* management frame protection */ | ||||
| 
 | ||||
| 	int wmm_last_param_set; | ||||
| 
 | ||||
| 	u8 use_4addr; | ||||
| }; | ||||
| 
 | ||||
| enum ieee80211_ibss_request { | ||||
| @ -461,8 +461,6 @@ struct ieee80211_sub_if_data { | ||||
| 	int force_unicast_rateidx; /* forced TX rateidx for unicast frames */ | ||||
| 	int max_ratectrl_rateidx; /* max TX rateidx for rate control */ | ||||
| 
 | ||||
| 	bool use_4addr; /* use 4-address frames */ | ||||
| 
 | ||||
| 	union { | ||||
| 		struct ieee80211_if_ap ap; | ||||
| 		struct ieee80211_if_wds wds; | ||||
| @ -581,7 +579,6 @@ struct ieee80211_local { | ||||
| 	/* number of interfaces with corresponding FIF_ flags */ | ||||
| 	int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll; | ||||
| 	unsigned int filter_flags; /* FIF_* */ | ||||
| 	struct iw_statistics wstats; | ||||
| 
 | ||||
| 	/* protects the aggregated multicast list and filter calls */ | ||||
| 	spinlock_t filter_lock; | ||||
| @ -771,8 +768,9 @@ IEEE80211_DEV_TO_SUB_IF(struct net_device *dev) | ||||
| 	return netdev_priv(dev); | ||||
| } | ||||
| 
 | ||||
| /* this struct represents 802.11n's RA/TID combination */ | ||||
| /* this struct represents 802.11n's RA/TID combination along with our vif */ | ||||
| struct ieee80211_ra_tid { | ||||
| 	struct ieee80211_vif *vif; | ||||
| 	u8 ra[ETH_ALEN]; | ||||
| 	u16 tid; | ||||
| }; | ||||
| @ -799,7 +797,7 @@ struct ieee802_11_elems { | ||||
| 	u8 *wmm_param; | ||||
| 	struct ieee80211_ht_cap *ht_cap_elem; | ||||
| 	struct ieee80211_ht_info *ht_info_elem; | ||||
| 	u8 *mesh_config; | ||||
| 	struct ieee80211_meshconf_ie *mesh_config; | ||||
| 	u8 *mesh_id; | ||||
| 	u8 *peer_link; | ||||
| 	u8 *preq; | ||||
| @ -827,7 +825,6 @@ struct ieee802_11_elems { | ||||
| 	u8 ext_supp_rates_len; | ||||
| 	u8 wmm_info_len; | ||||
| 	u8 wmm_param_len; | ||||
| 	u8 mesh_config_len; | ||||
| 	u8 mesh_id_len; | ||||
| 	u8 peer_link_len; | ||||
| 	u8 preq_len; | ||||
| @ -950,6 +947,18 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, | ||||
| netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | ||||
| 				       struct net_device *dev); | ||||
| 
 | ||||
| /*
 | ||||
|  * radiotap header for status frames | ||||
|  */ | ||||
| struct ieee80211_tx_status_rtap_hdr { | ||||
| 	struct ieee80211_radiotap_header hdr; | ||||
| 	u8 rate; | ||||
| 	u8 padding_for_rate; | ||||
| 	__le16 tx_flags; | ||||
| 	u8 data_retries; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| /* HT */ | ||||
| void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, | ||||
| 				       struct ieee80211_ht_cap *ht_cap_ie, | ||||
| @ -1017,8 +1026,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke | ||||
| 				     struct ieee80211_hdr *hdr, const u8 *tsc, | ||||
| 				     gfp_t gfp); | ||||
| void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); | ||||
| void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, | ||||
| 		      int encrypt); | ||||
| void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); | ||||
| void ieee802_11_parse_elems(u8 *start, size_t len, | ||||
| 			    struct ieee802_11_elems *elems); | ||||
| u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, | ||||
|  | ||||
| @ -752,7 +752,8 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, | ||||
| 		ieee80211_mandatory_rates(sdata->local, | ||||
| 			sdata->local->hw.conf.channel->band); | ||||
| 	sdata->drop_unencrypted = 0; | ||||
| 	sdata->use_4addr = 0; | ||||
| 	if (type == NL80211_IFTYPE_STATION) | ||||
| 		sdata->u.mgd.use_4addr = false; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -810,6 +811,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | ||||
| 	/* setup type-dependent data */ | ||||
| 	ieee80211_setup_sdata(sdata, type); | ||||
| 
 | ||||
| 	if (params) { | ||||
| 		ndev->ieee80211_ptr->use_4addr = params->use_4addr; | ||||
| 		if (type == NL80211_IFTYPE_STATION) | ||||
| 			sdata->u.mgd.use_4addr = params->use_4addr; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = register_netdevice(ndev); | ||||
| 	if (ret) | ||||
| 		goto fail; | ||||
| @ -820,9 +827,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | ||||
| 					    params->mesh_id_len, | ||||
| 					    params->mesh_id); | ||||
| 
 | ||||
| 	if (params && params->use_4addr >= 0) | ||||
| 		sdata->use_4addr = !!params->use_4addr; | ||||
| 
 | ||||
| 	mutex_lock(&local->iflist_mtx); | ||||
| 	list_add_tail_rcu(&sdata->list, &local->interfaces); | ||||
| 	mutex_unlock(&local->iflist_mtx); | ||||
|  | ||||
| @ -9,7 +9,6 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include <net/mac80211.h> | ||||
| #include <net/ieee80211_radiotap.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/netdevice.h> | ||||
| @ -30,26 +29,11 @@ | ||||
| #include "rate.h" | ||||
| #include "mesh.h" | ||||
| #include "wep.h" | ||||
| #include "wme.h" | ||||
| #include "aes_ccm.h" | ||||
| #include "led.h" | ||||
| #include "cfg.h" | ||||
| #include "debugfs.h" | ||||
| #include "debugfs_netdev.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * For seeing transmitted packets on monitor interfaces | ||||
|  * we have a radiotap header too. | ||||
|  */ | ||||
| struct ieee80211_tx_status_rtap_hdr { | ||||
| 	struct ieee80211_radiotap_header hdr; | ||||
| 	u8 rate; | ||||
| 	u8 padding_for_rate; | ||||
| 	__le16 tx_flags; | ||||
| 	u8 data_retries; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| void ieee80211_configure_filter(struct ieee80211_local *local) | ||||
| { | ||||
| 	u64 mc; | ||||
| @ -253,28 +237,6 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) | ||||
| 	       BSS_CHANGED_ERP_SLOT; | ||||
| } | ||||
| 
 | ||||
| void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, | ||||
| 				 struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	int tmp; | ||||
| 
 | ||||
| 	skb->pkt_type = IEEE80211_TX_STATUS_MSG; | ||||
| 	skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ? | ||||
| 		       &local->skb_queue : &local->skb_queue_unreliable, skb); | ||||
| 	tmp = skb_queue_len(&local->skb_queue) + | ||||
| 		skb_queue_len(&local->skb_queue_unreliable); | ||||
| 	while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && | ||||
| 	       (skb = skb_dequeue(&local->skb_queue_unreliable))) { | ||||
| 		dev_kfree_skb_irq(skb); | ||||
| 		tmp--; | ||||
| 		I802_DEBUG_INC(local->tx_status_drop); | ||||
| 	} | ||||
| 	tasklet_schedule(&local->tasklet); | ||||
| } | ||||
| EXPORT_SYMBOL(ieee80211_tx_status_irqsafe); | ||||
| 
 | ||||
| static void ieee80211_tasklet_handler(unsigned long data) | ||||
| { | ||||
| 	struct ieee80211_local *local = (struct ieee80211_local *) data; | ||||
| @ -296,14 +258,14 @@ static void ieee80211_tasklet_handler(unsigned long data) | ||||
| 			break; | ||||
| 		case IEEE80211_DELBA_MSG: | ||||
| 			ra_tid = (struct ieee80211_ra_tid *) &skb->cb; | ||||
| 			ieee80211_stop_tx_ba_cb(local_to_hw(local), | ||||
| 						ra_tid->ra, ra_tid->tid); | ||||
| 			ieee80211_stop_tx_ba_cb(ra_tid->vif, ra_tid->ra, | ||||
| 						ra_tid->tid); | ||||
| 			dev_kfree_skb(skb); | ||||
| 			break; | ||||
| 		case IEEE80211_ADDBA_MSG: | ||||
| 			ra_tid = (struct ieee80211_ra_tid *) &skb->cb; | ||||
| 			ieee80211_start_tx_ba_cb(local_to_hw(local), | ||||
| 						 ra_tid->ra, ra_tid->tid); | ||||
| 			ieee80211_start_tx_ba_cb(ra_tid->vif, ra_tid->ra, | ||||
| 						 ra_tid->tid); | ||||
| 			dev_kfree_skb(skb); | ||||
| 			break ; | ||||
| 		default: | ||||
| @ -315,299 +277,6 @@ static void ieee80211_tasklet_handler(unsigned long data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | ||||
| 					    struct sta_info *sta, | ||||
| 					    struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * XXX: This is temporary! | ||||
| 	 * | ||||
| 	 *	The problem here is that when we get here, the driver will | ||||
| 	 *	quite likely have pretty much overwritten info->control by | ||||
| 	 *	using info->driver_data or info->rate_driver_data. Thus, | ||||
| 	 *	when passing out the frame to the driver again, we would be | ||||
| 	 *	passing completely bogus data since the driver would then | ||||
| 	 *	expect a properly filled info->control. In mac80211 itself | ||||
| 	 *	the same problem occurs, since we need info->control.vif | ||||
| 	 *	internally. | ||||
| 	 * | ||||
| 	 *	To fix this, we should send the frame through TX processing | ||||
| 	 *	again. However, it's not that simple, since the frame will | ||||
| 	 *	have been software-encrypted (if applicable) already, and | ||||
| 	 *	encrypting it again doesn't do much good. So to properly do | ||||
| 	 *	that, we not only have to skip the actual 'raw' encryption | ||||
| 	 *	(key selection etc. still has to be done!) but also the | ||||
| 	 *	sequence number assignment since that impacts the crypto | ||||
| 	 *	encapsulation, of course. | ||||
| 	 * | ||||
| 	 *	Hence, for now, fix the bug by just dropping the frame. | ||||
| 	 */ | ||||
| 	goto drop; | ||||
| 
 | ||||
| 	sta->tx_filtered_count++; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Clear the TX filter mask for this STA when sending the next | ||||
| 	 * packet. If the STA went to power save mode, this will happen | ||||
| 	 * when it wakes up for the next time. | ||||
| 	 */ | ||||
| 	set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * This code races in the following way: | ||||
| 	 * | ||||
| 	 *  (1) STA sends frame indicating it will go to sleep and does so | ||||
| 	 *  (2) hardware/firmware adds STA to filter list, passes frame up | ||||
| 	 *  (3) hardware/firmware processes TX fifo and suppresses a frame | ||||
| 	 *  (4) we get TX status before having processed the frame and | ||||
| 	 *	knowing that the STA has gone to sleep. | ||||
| 	 * | ||||
| 	 * This is actually quite unlikely even when both those events are | ||||
| 	 * processed from interrupts coming in quickly after one another or | ||||
| 	 * even at the same time because we queue both TX status events and | ||||
| 	 * RX frames to be processed by a tasklet and process them in the | ||||
| 	 * same order that they were received or TX status last. Hence, there | ||||
| 	 * is no race as long as the frame RX is processed before the next TX | ||||
| 	 * status, which drivers can ensure, see below. | ||||
| 	 * | ||||
| 	 * Note that this can only happen if the hardware or firmware can | ||||
| 	 * actually add STAs to the filter list, if this is done by the | ||||
| 	 * driver in response to set_tim() (which will only reduce the race | ||||
| 	 * this whole filtering tries to solve, not completely solve it) | ||||
| 	 * this situation cannot happen. | ||||
| 	 * | ||||
| 	 * To completely solve this race drivers need to make sure that they | ||||
| 	 *  (a) don't mix the irq-safe/not irq-safe TX status/RX processing | ||||
| 	 *	functions and | ||||
| 	 *  (b) always process RX events before TX status events if ordering | ||||
| 	 *      can be unknown, for example with different interrupt status | ||||
| 	 *	bits. | ||||
| 	 */ | ||||
| 	if (test_sta_flags(sta, WLAN_STA_PS_STA) && | ||||
| 	    skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { | ||||
| 		skb_queue_tail(&sta->tx_filtered, skb); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!test_sta_flags(sta, WLAN_STA_PS_STA) && | ||||
| 	    !(info->flags & IEEE80211_TX_INTFL_RETRIED)) { | ||||
| 		/* Software retry the packet once */ | ||||
| 		info->flags |= IEEE80211_TX_INTFL_RETRIED; | ||||
| 		ieee80211_add_pending_skb(local, skb); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  drop: | ||||
| #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | ||||
| 	if (net_ratelimit()) | ||||
| 		printk(KERN_DEBUG "%s: dropped TX filtered frame, " | ||||
| 		       "queue_len=%d PS=%d @%lu\n", | ||||
| 		       wiphy_name(local->hw.wiphy), | ||||
| 		       skb_queue_len(&sta->tx_filtered), | ||||
| 		       !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies); | ||||
| #endif | ||||
| 	dev_kfree_skb(skb); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | ||||
| { | ||||
| 	struct sk_buff *skb2; | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||||
| 	struct ieee80211_local *local = hw_to_local(hw); | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	u16 frag, type; | ||||
| 	__le16 fc; | ||||
| 	struct ieee80211_supported_band *sband; | ||||
| 	struct ieee80211_tx_status_rtap_hdr *rthdr; | ||||
| 	struct ieee80211_sub_if_data *sdata; | ||||
| 	struct net_device *prev_dev = NULL; | ||||
| 	struct sta_info *sta; | ||||
| 	int retry_count = -1, i; | ||||
| 
 | ||||
| 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { | ||||
| 		/* the HW cannot have attempted that rate */ | ||||
| 		if (i >= hw->max_rates) { | ||||
| 			info->status.rates[i].idx = -1; | ||||
| 			info->status.rates[i].count = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		retry_count += info->status.rates[i].count; | ||||
| 	} | ||||
| 	if (retry_count < 0) | ||||
| 		retry_count = 0; | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 
 | ||||
| 	sband = local->hw.wiphy->bands[info->band]; | ||||
| 
 | ||||
| 	sta = sta_info_get(local, hdr->addr1); | ||||
| 
 | ||||
| 	if (sta) { | ||||
| 		if (!(info->flags & IEEE80211_TX_STAT_ACK) && | ||||
| 		    test_sta_flags(sta, WLAN_STA_PS_STA)) { | ||||
| 			/*
 | ||||
| 			 * The STA is in power save mode, so assume | ||||
| 			 * that this TX packet failed because of that. | ||||
| 			 */ | ||||
| 			ieee80211_handle_filtered_frame(local, sta, skb); | ||||
| 			rcu_read_unlock(); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		fc = hdr->frame_control; | ||||
| 
 | ||||
| 		if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) && | ||||
| 		    (ieee80211_is_data_qos(fc))) { | ||||
| 			u16 tid, ssn; | ||||
| 			u8 *qc; | ||||
| 
 | ||||
| 			qc = ieee80211_get_qos_ctl(hdr); | ||||
| 			tid = qc[0] & 0xf; | ||||
| 			ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) | ||||
| 						& IEEE80211_SCTL_SEQ); | ||||
| 			ieee80211_send_bar(sta->sdata, hdr->addr1, | ||||
| 					   tid, ssn); | ||||
| 		} | ||||
| 
 | ||||
| 		if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { | ||||
| 			ieee80211_handle_filtered_frame(local, sta, skb); | ||||
| 			rcu_read_unlock(); | ||||
| 			return; | ||||
| 		} else { | ||||
| 			if (!(info->flags & IEEE80211_TX_STAT_ACK)) | ||||
| 				sta->tx_retry_failed++; | ||||
| 			sta->tx_retry_count += retry_count; | ||||
| 		} | ||||
| 
 | ||||
| 		rate_control_tx_status(local, sband, sta, skb); | ||||
| 		if (ieee80211_vif_is_mesh(&sta->sdata->vif)) | ||||
| 			ieee80211s_update_metric(local, sta, skb); | ||||
| 	} | ||||
| 
 | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	ieee80211_led_tx(local, 0); | ||||
| 
 | ||||
| 	/* SNMP counters
 | ||||
| 	 * Fragments are passed to low-level drivers as separate skbs, so these | ||||
| 	 * are actually fragments, not frames. Update frame counters only for | ||||
| 	 * the first fragment of the frame. */ | ||||
| 
 | ||||
| 	frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; | ||||
| 	type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; | ||||
| 
 | ||||
| 	if (info->flags & IEEE80211_TX_STAT_ACK) { | ||||
| 		if (frag == 0) { | ||||
| 			local->dot11TransmittedFrameCount++; | ||||
| 			if (is_multicast_ether_addr(hdr->addr1)) | ||||
| 				local->dot11MulticastTransmittedFrameCount++; | ||||
| 			if (retry_count > 0) | ||||
| 				local->dot11RetryCount++; | ||||
| 			if (retry_count > 1) | ||||
| 				local->dot11MultipleRetryCount++; | ||||
| 		} | ||||
| 
 | ||||
| 		/* This counter shall be incremented for an acknowledged MPDU
 | ||||
| 		 * with an individual address in the address 1 field or an MPDU | ||||
| 		 * with a multicast address in the address 1 field of type Data | ||||
| 		 * or Management. */ | ||||
| 		if (!is_multicast_ether_addr(hdr->addr1) || | ||||
| 		    type == IEEE80211_FTYPE_DATA || | ||||
| 		    type == IEEE80211_FTYPE_MGMT) | ||||
| 			local->dot11TransmittedFragmentCount++; | ||||
| 	} else { | ||||
| 		if (frag == 0) | ||||
| 			local->dot11FailedCount++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* this was a transmitted frame, but now we want to reuse it */ | ||||
| 	skb_orphan(skb); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * This is a bit racy but we can avoid a lot of work | ||||
| 	 * with this test... | ||||
| 	 */ | ||||
| 	if (!local->monitors && !local->cooked_mntrs) { | ||||
| 		dev_kfree_skb(skb); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* send frame to monitor interfaces now */ | ||||
| 
 | ||||
| 	if (skb_headroom(skb) < sizeof(*rthdr)) { | ||||
| 		printk(KERN_ERR "ieee80211_tx_status: headroom too small\n"); | ||||
| 		dev_kfree_skb(skb); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	rthdr = (struct ieee80211_tx_status_rtap_hdr *) | ||||
| 				skb_push(skb, sizeof(*rthdr)); | ||||
| 
 | ||||
| 	memset(rthdr, 0, sizeof(*rthdr)); | ||||
| 	rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); | ||||
| 	rthdr->hdr.it_present = | ||||
| 		cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) | | ||||
| 			    (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | | ||||
| 			    (1 << IEEE80211_RADIOTAP_RATE)); | ||||
| 
 | ||||
| 	if (!(info->flags & IEEE80211_TX_STAT_ACK) && | ||||
| 	    !is_multicast_ether_addr(hdr->addr1)) | ||||
| 		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * XXX: Once radiotap gets the bitmap reset thing the vendor | ||||
| 	 *	extensions proposal contains, we can actually report | ||||
| 	 *	the whole set of tries we did. | ||||
| 	 */ | ||||
| 	if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || | ||||
| 	    (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) | ||||
| 		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS); | ||||
| 	else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) | ||||
| 		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS); | ||||
| 	if (info->status.rates[0].idx >= 0 && | ||||
| 	    !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) | ||||
| 		rthdr->rate = sband->bitrates[ | ||||
| 				info->status.rates[0].idx].bitrate / 5; | ||||
| 
 | ||||
| 	/* for now report the total retry_count */ | ||||
| 	rthdr->data_retries = retry_count; | ||||
| 
 | ||||
| 	/* XXX: is this sufficient for BPF? */ | ||||
| 	skb_set_mac_header(skb, 0); | ||||
| 	skb->ip_summed = CHECKSUM_UNNECESSARY; | ||||
| 	skb->pkt_type = PACKET_OTHERHOST; | ||||
| 	skb->protocol = htons(ETH_P_802_2); | ||||
| 	memset(skb->cb, 0, sizeof(skb->cb)); | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	list_for_each_entry_rcu(sdata, &local->interfaces, list) { | ||||
| 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { | ||||
| 			if (!netif_running(sdata->dev)) | ||||
| 				continue; | ||||
| 
 | ||||
| 			if (prev_dev) { | ||||
| 				skb2 = skb_clone(skb, GFP_ATOMIC); | ||||
| 				if (skb2) { | ||||
| 					skb2->dev = prev_dev; | ||||
| 					netif_rx(skb2); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			prev_dev = sdata->dev; | ||||
| 		} | ||||
| 	} | ||||
| 	if (prev_dev) { | ||||
| 		skb->dev = prev_dev; | ||||
| 		netif_rx(skb); | ||||
| 		skb = NULL; | ||||
| 	} | ||||
| 	rcu_read_unlock(); | ||||
| 	dev_kfree_skb(skb); | ||||
| } | ||||
| EXPORT_SYMBOL(ieee80211_tx_status); | ||||
| 
 | ||||
| static void ieee80211_restart_work(struct work_struct *work) | ||||
| { | ||||
| 	struct ieee80211_local *local = | ||||
| @ -659,7 +328,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | ||||
| 	if (!wiphy) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	wiphy->netnsok = true; | ||||
| 	wiphy->flags |= WIPHY_FLAG_NETNS_OK | | ||||
| 			WIPHY_FLAG_4ADDR_AP | | ||||
| 			WIPHY_FLAG_4ADDR_STATION; | ||||
| 	wiphy->privid = mac80211_wiphy_privid; | ||||
| 
 | ||||
| 	/* Yes, putting cfg80211_bss into ieee80211_bss is a hack */ | ||||
|  | ||||
| @ -16,12 +16,6 @@ | ||||
| #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) | ||||
| #define IEEE80211_MESH_RANN_INTERVAL	     (1 * HZ) | ||||
| 
 | ||||
| #define MESHCONF_PP_OFFSET 	0		/* Path Selection Protocol */ | ||||
| #define MESHCONF_PM_OFFSET	1		/* Path Selection Metric   */ | ||||
| #define MESHCONF_CC_OFFSET	2		/* Congestion Control Mode */ | ||||
| #define MESHCONF_SP_OFFSET	3		/* Synchronization Protocol */ | ||||
| #define MESHCONF_AUTH_OFFSET	4		/* Authentication Protocol */ | ||||
| #define MESHCONF_CAPAB_OFFSET 	6 | ||||
| #define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01 | ||||
| #define MESHCONF_CAPAB_FORWARDING    0x08 | ||||
| 
 | ||||
| @ -87,12 +81,11 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat | ||||
| 	 */ | ||||
| 	if (ifmsh->mesh_id_len == ie->mesh_id_len && | ||||
| 		memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && | ||||
| 		(ifmsh->mesh_pp_id == *(ie->mesh_config + MESHCONF_PP_OFFSET))&& | ||||
| 		(ifmsh->mesh_pm_id == *(ie->mesh_config + MESHCONF_PM_OFFSET))&& | ||||
| 		(ifmsh->mesh_cc_id == *(ie->mesh_config + MESHCONF_CC_OFFSET))&& | ||||
| 		(ifmsh->mesh_sp_id == *(ie->mesh_config + MESHCONF_SP_OFFSET))&& | ||||
| 		(ifmsh->mesh_auth_id == *(ie->mesh_config + | ||||
| 		    MESHCONF_AUTH_OFFSET))) | ||||
| 		(ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && | ||||
| 		(ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && | ||||
| 		(ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && | ||||
| 		(ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && | ||||
| 		(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)) | ||||
| 		return true; | ||||
| 
 | ||||
| 	return false; | ||||
| @ -105,7 +98,7 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat | ||||
|  */ | ||||
| bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie) | ||||
| { | ||||
| 	return (*(ie->mesh_config + MESHCONF_CAPAB_OFFSET) & | ||||
| 	return (ie->mesh_config->meshconf_cap & | ||||
| 	    MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; | ||||
| } | ||||
| 
 | ||||
| @ -262,9 +255,9 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||||
| 	if (sdata->u.mesh.mesh_id_len) | ||||
| 		memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len); | ||||
| 
 | ||||
| 	pos = skb_put(skb, 2 + IEEE80211_MESH_CONFIG_LEN); | ||||
| 	pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie)); | ||||
| 	*pos++ = WLAN_EID_MESH_CONFIG; | ||||
| 	*pos++ = IEEE80211_MESH_CONFIG_LEN; | ||||
| 	*pos++ = sizeof(struct ieee80211_meshconf_ie); | ||||
| 
 | ||||
| 	/* Active path selection protocol ID */ | ||||
| 	*pos++ = sdata->u.mesh.mesh_pp_id; | ||||
| @ -394,8 +387,9 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh) | ||||
|  * | ||||
|  * Return the length of the 802.11 (does not include a mesh control header) | ||||
|  */ | ||||
| int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char | ||||
| 		*meshda, char *meshsa) { | ||||
| int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, | ||||
| 				  const u8 *meshda, const u8 *meshsa) | ||||
| { | ||||
| 	if (is_multicast_ether_addr(meshda)) { | ||||
| 		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); | ||||
| 		/* DA TA SA */ | ||||
|  | ||||
| @ -220,7 +220,7 @@ struct mesh_rmc { | ||||
| /* Public interfaces */ | ||||
| /* Various */ | ||||
| int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, | ||||
| 		char *da, char *sa); | ||||
| 				  const u8 *da, const u8 *sa); | ||||
| int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, | ||||
| 		struct ieee80211_sub_if_data *sdata, char *addr4, | ||||
| 		char *addr5, char *addr6); | ||||
| @ -284,7 +284,7 @@ u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, | ||||
| 		struct mesh_table *tbl); | ||||
| /* Mesh paths */ | ||||
| int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, | ||||
| 		u8 *ra, struct ieee80211_sub_if_data *sdata); | ||||
| 		       const u8 *ra, struct ieee80211_sub_if_data *sdata); | ||||
| void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta); | ||||
| void mesh_path_flush_pending(struct mesh_path *mpath); | ||||
| void mesh_path_tx_pending(struct mesh_path *mpath); | ||||
|  | ||||
| @ -101,10 +101,12 @@ enum mpath_frame_type { | ||||
| 	MPATH_RANN | ||||
| }; | ||||
| 
 | ||||
| static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||||
| 
 | ||||
| static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, | ||||
| 		u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target, | ||||
| 		__le32 target_sn, u8 *da, u8 hop_count, u8 ttl,__le32 lifetime, | ||||
| 		__le32 metric, __le32 preq_id, | ||||
| 		__le32 target_sn, const u8 *da, u8 hop_count, u8 ttl, | ||||
| 		__le32 lifetime, __le32 metric, __le32 preq_id, | ||||
| 		struct ieee80211_sub_if_data *sdata) | ||||
| { | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| @ -185,7 +187,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, | ||||
| 		memcpy(pos, &target_sn, 4); | ||||
| 	} | ||||
| 
 | ||||
| 	ieee80211_tx_skb(sdata, skb, 1); | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -198,8 +200,8 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, | ||||
|  * @ra: node this frame is addressed to | ||||
|  */ | ||||
| int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, | ||||
| 		__le16 target_rcode, u8 *ra, | ||||
| 		struct ieee80211_sub_if_data *sdata) | ||||
| 		       __le16 target_rcode, const u8 *ra, | ||||
| 		       struct ieee80211_sub_if_data *sdata) | ||||
| { | ||||
| 	struct ieee80211_local *local = sdata->local; | ||||
| 	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); | ||||
| @ -248,7 +250,7 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, | ||||
| 	pos += 4; | ||||
| 	memcpy(pos, &target_rcode, 2); | ||||
| 
 | ||||
| 	ieee80211_tx_skb(sdata, skb, 1); | ||||
| 	ieee80211_tx_skb(sdata, skb); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -548,7 +550,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, | ||||
| 		hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; | ||||
| 		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, | ||||
| 				cpu_to_le32(orig_sn), target_flags, target_addr, | ||||
| 				cpu_to_le32(target_sn), sdata->dev->broadcast, | ||||
| 				cpu_to_le32(target_sn), broadcast_addr, | ||||
| 				hopcount, ttl, cpu_to_le32(lifetime), | ||||
| 				cpu_to_le32(metric), cpu_to_le32(preq_id), | ||||
| 				sdata); | ||||
| @ -660,7 +662,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, | ||||
| 			spin_unlock_bh(&mpath->state_lock); | ||||
| 			mesh_path_error_tx(ttl, target_addr, cpu_to_le32(target_sn), | ||||
| 					   cpu_to_le16(target_rcode), | ||||
| 					   sdata->dev->broadcast, sdata); | ||||
| 					   broadcast_addr, sdata); | ||||
| 		} else | ||||
| 			spin_unlock_bh(&mpath->state_lock); | ||||
| 	} | ||||
| @ -709,7 +711,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, | ||||
| 	if (mpath->sn < orig_sn) { | ||||
| 		mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, | ||||
| 				       cpu_to_le32(orig_sn), | ||||
| 				       0, NULL, 0, sdata->dev->broadcast, | ||||
| 				       0, NULL, 0, broadcast_addr, | ||||
| 				       hopcount, ttl, 0, | ||||
| 				       cpu_to_le32(metric + mpath->metric), | ||||
| 				       0, sdata); | ||||
| @ -890,7 +892,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) | ||||
| 	spin_unlock_bh(&mpath->state_lock); | ||||
| 	mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->dev->dev_addr, | ||||
| 			cpu_to_le32(ifmsh->sn), target_flags, mpath->dst, | ||||
| 			cpu_to_le32(mpath->sn), sdata->dev->broadcast, 0, | ||||
| 			cpu_to_le32(mpath->sn), broadcast_addr, 0, | ||||
| 			ttl, cpu_to_le32(lifetime), 0, | ||||
| 			cpu_to_le32(ifmsh->preq_id++), sdata); | ||||
| 	mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout); | ||||
| @ -1011,6 +1013,6 @@ mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) | ||||
| 
 | ||||
| 	mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->dev->dev_addr, | ||||
| 			       cpu_to_le32(++ifmsh->sn), | ||||
| 			       0, NULL, 0, sdata->dev->broadcast, | ||||
| 			       0, NULL, 0, broadcast_addr, | ||||
| 			       0, MESH_TTL, 0, 0, 0, sdata); | ||||
| } | ||||
|  | ||||
| @ -449,6 +449,7 @@ err_path_alloc: | ||||
|  */ | ||||
| void mesh_plink_broken(struct sta_info *sta) | ||||
| { | ||||
| 	static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||||
| 	struct mesh_path *mpath; | ||||
| 	struct mpath_node *node; | ||||
| 	struct hlist_node *p; | ||||
| @ -467,8 +468,8 @@ void mesh_plink_broken(struct sta_info *sta) | ||||
| 			spin_unlock_bh(&mpath->state_lock); | ||||
| 			mesh_path_error_tx(MESH_TTL, mpath->dst, | ||||
| 					cpu_to_le32(mpath->sn), | ||||
| 					PERR_RCODE_DEST_UNREACH, | ||||
| 					sdata->dev->broadcast, sdata); | ||||
| 					cpu_to_le16(PERR_RCODE_DEST_UNREACH), | ||||
| 					bcast, sdata); | ||||
| 		} else | ||||
| 		spin_unlock_bh(&mpath->state_lock); | ||||
| 	} | ||||
| @ -613,7 +614,7 @@ void mesh_path_discard_frame(struct sk_buff *skb, | ||||
| 		if (mpath) | ||||
| 			sn = ++mpath->sn; | ||||
| 		mesh_path_error_tx(MESH_TTL, skb->data, cpu_to_le32(sn), | ||||
| 				   PERR_RCODE_NO_ROUTE, ra, sdata); | ||||
| 				   cpu_to_le16(PERR_RCODE_NO_ROUTE), ra, sdata); | ||||
| 	} | ||||
| 
 | ||||
| 	kfree_skb(skb); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user