Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
		
						commit
						a8679be207
					
				| @ -500,5 +500,6 @@ source "drivers/net/wireless/b43legacy/Kconfig" | ||||
| source "drivers/net/wireless/zd1211rw/Kconfig" | ||||
| source "drivers/net/wireless/rt2x00/Kconfig" | ||||
| source "drivers/net/wireless/orinoco/Kconfig" | ||||
| source "drivers/net/wireless/wl12xx/Kconfig" | ||||
| 
 | ||||
| endmenu | ||||
|  | ||||
| @ -58,3 +58,5 @@ obj-$(CONFIG_P54_COMMON)	+= p54/ | ||||
| obj-$(CONFIG_ATH_COMMON)	+= ath/ | ||||
| 
 | ||||
| obj-$(CONFIG_MAC80211_HWSIM)	+= mac80211_hwsim.o | ||||
| 
 | ||||
| obj-$(CONFIG_WL12XX)	+= wl12xx/ | ||||
|  | ||||
| @ -1311,18 +1311,20 @@ static int adm8211_config(struct ieee80211_hw *dev, u32 changed) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int adm8211_config_interface(struct ieee80211_hw *dev, | ||||
| 				    struct ieee80211_vif *vif, | ||||
| 				    struct ieee80211_if_conf *conf) | ||||
| static void adm8211_bss_info_changed(struct ieee80211_hw *dev, | ||||
| 				     struct ieee80211_vif *vif, | ||||
| 				     struct ieee80211_bss_conf *conf, | ||||
| 				     u32 changes) | ||||
| { | ||||
| 	struct adm8211_priv *priv = dev->priv; | ||||
| 
 | ||||
| 	if (!(changes & BSS_CHANGED_BSSID)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { | ||||
| 		adm8211_set_bssid(dev, conf->bssid); | ||||
| 		memcpy(priv->bssid, conf->bssid, ETH_ALEN); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void adm8211_configure_filter(struct ieee80211_hw *dev, | ||||
| @ -1753,7 +1755,7 @@ static const struct ieee80211_ops adm8211_ops = { | ||||
| 	.add_interface		= adm8211_add_interface, | ||||
| 	.remove_interface	= adm8211_remove_interface, | ||||
| 	.config			= adm8211_config, | ||||
| 	.config_interface	= adm8211_config_interface, | ||||
| 	.bss_info_changed	= adm8211_bss_info_changed, | ||||
| 	.configure_filter	= adm8211_configure_filter, | ||||
| 	.get_stats		= adm8211_get_stats, | ||||
| 	.get_tx_stats		= adm8211_get_tx_stats, | ||||
|  | ||||
| @ -1965,13 +1965,18 @@ static int at76_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int at76_config_interface(struct ieee80211_hw *hw, | ||||
| 				 struct ieee80211_vif *vif, | ||||
| 				 struct ieee80211_if_conf *conf) | ||||
| static void at76_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 				  struct ieee80211_vif *vif, | ||||
| 				  struct ieee80211_bss_conf *conf, | ||||
| 				  u32 changed) | ||||
| { | ||||
| 	struct at76_priv *priv = hw->priv; | ||||
| 
 | ||||
| 	at76_dbg(DBG_MAC80211, "%s():", __func__); | ||||
| 
 | ||||
| 	if (!(changed & BSS_CHANGED_BSSID)) | ||||
| 		return; | ||||
| 
 | ||||
| 	at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); | ||||
| 
 | ||||
| 	mutex_lock(&priv->mtx); | ||||
| @ -1983,8 +1988,6 @@ static int at76_config_interface(struct ieee80211_hw *hw, | ||||
| 		at76_join(priv); | ||||
| 
 | ||||
| 	mutex_unlock(&priv->mtx); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* must be atomic */ | ||||
| @ -2076,7 +2079,7 @@ static const struct ieee80211_ops at76_ops = { | ||||
| 	.add_interface = at76_add_interface, | ||||
| 	.remove_interface = at76_remove_interface, | ||||
| 	.config = at76_config, | ||||
| 	.config_interface = at76_config_interface, | ||||
| 	.bss_info_changed = at76_bss_info_changed, | ||||
| 	.configure_filter = at76_configure_filter, | ||||
| 	.start = at76_mac80211_start, | ||||
| 	.stop = at76_mac80211_stop, | ||||
|  | ||||
| @ -207,6 +207,8 @@ enum ar9170_cmd { | ||||
| #define AR9170_MAC_REG_AC1_AC0_TXOP		(AR9170_MAC_REG_BASE + 0xB44) | ||||
| #define AR9170_MAC_REG_AC3_AC2_TXOP		(AR9170_MAC_REG_BASE + 0xB48) | ||||
| 
 | ||||
| #define AR9170_MAC_REG_AMPDU_SET		(AR9170_MAC_REG_BASE + 0xba0) | ||||
| 
 | ||||
| #define AR9170_MAC_REG_ACK_TABLE		(AR9170_MAC_REG_BASE + 0xC00) | ||||
| #define AR9170_MAC_REG_AMPDU_RX_THRESH		(AR9170_MAC_REG_BASE + 0xC50) | ||||
| 
 | ||||
|  | ||||
| @ -72,6 +72,24 @@ int ar9170_set_qos(struct ar9170 *ar) | ||||
| 	return ar9170_regwrite_result(); | ||||
| } | ||||
| 
 | ||||
| static int ar9170_set_ampdu_density(struct ar9170 *ar, u8 mpdudensity) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	/* don't allow AMPDU density > 8us */ | ||||
| 	if (mpdudensity > 6) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* Watch out! Otus uses slightly different density values. */ | ||||
| 	val = 0x140a00 | (mpdudensity ? (mpdudensity + 1) : 0); | ||||
| 
 | ||||
| 	ar9170_regwrite_begin(ar); | ||||
| 	ar9170_regwrite(AR9170_MAC_REG_AMPDU_SET, val); | ||||
| 	ar9170_regwrite_finish(); | ||||
| 
 | ||||
| 	return ar9170_regwrite_result(); | ||||
| } | ||||
| 
 | ||||
| int ar9170_init_mac(struct ar9170 *ar) | ||||
| { | ||||
| 	ar9170_regwrite_begin(ar); | ||||
| @ -265,9 +283,9 @@ int ar9170_set_operating_mode(struct ar9170 *ar) | ||||
| 		case NL80211_IFTYPE_ADHOC: | ||||
| 			pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS; | ||||
| 			break; | ||||
| /*		case NL80211_IFTYPE_AP:
 | ||||
| 		case NL80211_IFTYPE_AP: | ||||
| 			pm_mode |= AR9170_MAC_REG_POWERMGT_AP; | ||||
| 			break;*/ | ||||
| 			break; | ||||
| 		case NL80211_IFTYPE_WDS: | ||||
| 			pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS; | ||||
| 			break; | ||||
| @ -296,6 +314,11 @@ int ar9170_set_operating_mode(struct ar9170 *ar) | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	/* set AMPDU density to 8us. */ | ||||
| 	err = ar9170_set_ampdu_density(ar, 6); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	ar9170_regwrite_begin(ar); | ||||
| 
 | ||||
| 	ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode); | ||||
| @ -316,9 +339,9 @@ int ar9170_set_beacon_timers(struct ar9170 *ar) | ||||
| 	u32 v = 0; | ||||
| 	u32 pretbtt = 0; | ||||
| 
 | ||||
| 	v |= ar->hw->conf.beacon_int; | ||||
| 
 | ||||
| 	if (ar->vif) { | ||||
| 		v |= ar->vif->bss_conf.beacon_int; | ||||
| 
 | ||||
| 		switch (ar->vif->type) { | ||||
| 		case NL80211_IFTYPE_MESH_POINT: | ||||
| 		case NL80211_IFTYPE_ADHOC: | ||||
| @ -326,7 +349,7 @@ int ar9170_set_beacon_timers(struct ar9170 *ar) | ||||
| 			break; | ||||
| 		case NL80211_IFTYPE_AP: | ||||
| 			v |= BIT(24); | ||||
| 			pretbtt = (ar->hw->conf.beacon_int - 6) << 16; | ||||
| 			pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
|  | ||||
| @ -151,8 +151,8 @@ static struct ieee80211_channel ar9170_5ghz_chantable[] = { | ||||
| 			  IEEE80211_HT_CAP_SGI_40 |			\ | ||||
| 			  IEEE80211_HT_CAP_DSSSCCK40 |			\ | ||||
| 			  IEEE80211_HT_CAP_SM_PS,			\ | ||||
| 	.ampdu_factor	= 3, /* ?? */					\ | ||||
| 	.ampdu_density	= 7, /* ?? */					\ | ||||
| 	.ampdu_factor	= 3,						\ | ||||
| 	.ampdu_density	= 6,						\ | ||||
| 	.mcs		= {						\ | ||||
| 		.rx_mask = { 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, },	\ | ||||
| 	},								\ | ||||
| @ -1337,7 +1337,7 @@ static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) { | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT) { | ||||
| 		err = ar9170_set_beacon_timers(ar); | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| @ -1360,33 +1360,6 @@ out: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int ar9170_op_config_interface(struct ieee80211_hw *hw, | ||||
| 				      struct ieee80211_vif *vif, | ||||
| 				      struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct ar9170 *ar = hw->priv; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	mutex_lock(&ar->mutex); | ||||
| 
 | ||||
| 	if (conf->changed & IEEE80211_IFCC_BSSID) { | ||||
| 		memcpy(ar->bssid, conf->bssid, ETH_ALEN); | ||||
| 		err = ar9170_set_operating_mode(ar); | ||||
| 	} | ||||
| 
 | ||||
| 	if (conf->changed & IEEE80211_IFCC_BEACON) { | ||||
| 		err = ar9170_update_beacon(ar); | ||||
| 
 | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| 		err = ar9170_set_beacon_timers(ar); | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&ar->mutex); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void ar9170_set_filters(struct work_struct *work) | ||||
| { | ||||
| 	struct ar9170 *ar = container_of(work, struct ar9170, | ||||
| @ -1488,6 +1461,17 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 
 | ||||
| 	mutex_lock(&ar->mutex); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 		err = ar9170_set_operating_mode(ar); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) { | ||||
| 		err = ar9170_update_beacon(ar); | ||||
| 		if (!err) | ||||
| 			ar9170_set_beacon_timers(ar); | ||||
| 	} | ||||
| 
 | ||||
| 	ar9170_regwrite_begin(ar); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ASSOC) { | ||||
| @ -1499,6 +1483,9 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| #endif /* CONFIG_AR9170_LEDS */ | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT) | ||||
| 		err = ar9170_set_beacon_timers(ar); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_HT) { | ||||
| 		/* TODO */ | ||||
| 		err = 0; | ||||
| @ -1793,7 +1780,6 @@ static const struct ieee80211_ops ar9170_ops = { | ||||
| 	.add_interface		= ar9170_op_add_interface, | ||||
| 	.remove_interface	= ar9170_op_remove_interface, | ||||
| 	.config			= ar9170_op_config, | ||||
| 	.config_interface	= ar9170_op_config_interface, | ||||
| 	.configure_filter	= ar9170_op_configure_filter, | ||||
| 	.conf_tx		= ar9170_conf_tx, | ||||
| 	.bss_info_changed	= ar9170_op_bss_info_changed, | ||||
|  | ||||
| @ -350,7 +350,7 @@ static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd, | ||||
| 		goto err_unbuf; | ||||
| 	} | ||||
| 
 | ||||
| 	if (outlen >= 0 && aru->readlen != outlen) { | ||||
| 	if (aru->readlen != outlen) { | ||||
| 		err = -EMSGSIZE; | ||||
| 		goto err_unbuf; | ||||
| 	} | ||||
| @ -689,6 +689,9 @@ static int ar9170_usb_probe(struct usb_interface *intf, | ||||
| 	aru->common.exec_cmd = ar9170_usb_exec_cmd; | ||||
| 	aru->common.callback_cmd = ar9170_usb_callback_cmd; | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| 	udev->reset_resume = 1; | ||||
| #endif | ||||
| 	err = ar9170_usb_reset(aru); | ||||
| 	if (err) | ||||
| 		goto err_freehw; | ||||
| @ -805,6 +808,7 @@ static struct usb_driver ar9170_driver = { | ||||
| #ifdef CONFIG_PM | ||||
| 	.suspend = ar9170_suspend, | ||||
| 	.resume = ar9170_resume, | ||||
| 	.reset_resume = ar9170_resume, | ||||
| #endif /* CONFIG_PM */ | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -209,7 +209,6 @@ | ||||
| #define AR5K_TUNE_MAX_TXPOWER			63 | ||||
| #define AR5K_TUNE_DEFAULT_TXPOWER		25 | ||||
| #define AR5K_TUNE_TPC_TXPOWER			false | ||||
| #define AR5K_TUNE_ANT_DIVERSITY			true | ||||
| #define AR5K_TUNE_HWTXTRIES			4 | ||||
| 
 | ||||
| #define AR5K_INIT_CARR_SENSE_EN			1 | ||||
| @ -420,6 +419,17 @@ enum ath5k_driver_mode { | ||||
| 	AR5K_MODE_MAX		=	5 | ||||
| }; | ||||
| 
 | ||||
| enum ath5k_ant_mode { | ||||
| 	AR5K_ANTMODE_DEFAULT	= 0,	/* default antenna setup */ | ||||
| 	AR5K_ANTMODE_FIXED_A	= 1,	/* only antenna A is present */ | ||||
| 	AR5K_ANTMODE_FIXED_B	= 2,	/* only antenna B is present */ | ||||
| 	AR5K_ANTMODE_SINGLE_AP	= 3,	/* sta locked on a single ap */ | ||||
| 	AR5K_ANTMODE_SECTOR_AP	= 4,	/* AP with tx antenna set on tx desc */ | ||||
| 	AR5K_ANTMODE_SECTOR_STA	= 5,	/* STA with tx antenna set on tx desc */ | ||||
| 	AR5K_ANTMODE_DEBUG	= 6,	/* Debug mode -A -> Rx, B-> Tx- */ | ||||
| 	AR5K_ANTMODE_MAX, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /****************\
 | ||||
|   TX DEFINITIONS | ||||
| @ -1051,8 +1061,11 @@ struct ath5k_hw { | ||||
| 	bool			ah_software_retry; | ||||
| 	u32			ah_limit_tx_retries; | ||||
| 
 | ||||
| 	u32			ah_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; | ||||
| 	bool			ah_ant_diversity; | ||||
| 	/* Antenna Control */ | ||||
| 	u32			ah_ant_ctl[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; | ||||
| 	u8			ah_ant_mode; | ||||
| 	u8			ah_tx_ant; | ||||
| 	u8			ah_def_ant; | ||||
| 
 | ||||
| 	u8			ah_sta_id[ETH_ALEN]; | ||||
| 
 | ||||
| @ -1100,11 +1113,12 @@ struct ath5k_hw { | ||||
| 		/* Values in 0.25dB units */ | ||||
| 		s16		txp_min_pwr; | ||||
| 		s16		txp_max_pwr; | ||||
| 		/* Values in 0.5dB units */ | ||||
| 		s16		txp_offset; | ||||
| 		s16		txp_ofdm; | ||||
| 		/* Values in dB units */ | ||||
| 		s16		txp_cck_ofdm_pwr_delta; | ||||
| 		s16		txp_cck_ofdm_gainf_delta; | ||||
| 		/* Value in dB units */ | ||||
| 		s16		txp_cck_ofdm_pwr_delta; | ||||
| 	} ah_txpower; | ||||
| 
 | ||||
| 	struct { | ||||
| @ -1264,14 +1278,21 @@ extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *chann | ||||
| /* PHY calibration */ | ||||
| extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); | ||||
| extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); | ||||
| /* Spur mitigation */ | ||||
| bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, | ||||
| 				struct ieee80211_channel *channel); | ||||
| void ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, | ||||
| 				struct ieee80211_channel *channel); | ||||
| /* Misc PHY functions */ | ||||
| extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan); | ||||
| extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant); | ||||
| extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah); | ||||
| extern int ath5k_hw_phy_disable(struct ath5k_hw *ah); | ||||
| /* Antenna control */ | ||||
| extern void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode); | ||||
| extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant); | ||||
| extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah); | ||||
| /* TX power setup */ | ||||
| extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 ee_mode, u8 txpower); | ||||
| extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 ee_mode, u8 txpower); | ||||
| extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower); | ||||
| 
 | ||||
| /*
 | ||||
|  * Functions used internaly | ||||
|  | ||||
| @ -133,7 +133,6 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) | ||||
| 	ah->ah_cw_min = AR5K_TUNE_CWMIN; | ||||
| 	ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; | ||||
| 	ah->ah_software_retry = false; | ||||
| 	ah->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set the mac version based on the pci id | ||||
|  | ||||
| @ -227,9 +227,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, | ||||
| static void ath5k_remove_interface(struct ieee80211_hw *hw, | ||||
| 		struct ieee80211_if_init_conf *conf); | ||||
| static int ath5k_config(struct ieee80211_hw *hw, u32 changed); | ||||
| static int ath5k_config_interface(struct ieee80211_hw *hw, | ||||
| 		struct ieee80211_vif *vif, | ||||
| 		struct ieee80211_if_conf *conf); | ||||
| static void ath5k_configure_filter(struct ieee80211_hw *hw, | ||||
| 		unsigned int changed_flags, | ||||
| 		unsigned int *new_flags, | ||||
| @ -259,7 +256,6 @@ static const struct ieee80211_ops ath5k_hw_ops = { | ||||
| 	.add_interface 	= ath5k_add_interface, | ||||
| 	.remove_interface = ath5k_remove_interface, | ||||
| 	.config 	= ath5k_config, | ||||
| 	.config_interface = ath5k_config_interface, | ||||
| 	.configure_filter = ath5k_configure_filter, | ||||
| 	.set_key 	= ath5k_set_key, | ||||
| 	.get_stats 	= ath5k_get_stats, | ||||
| @ -520,6 +516,7 @@ ath5k_pci_probe(struct pci_dev *pdev, | ||||
| 		    IEEE80211_HW_NOISE_DBM; | ||||
| 
 | ||||
| 	hw->wiphy->interface_modes = | ||||
| 		BIT(NL80211_IFTYPE_AP) | | ||||
| 		BIT(NL80211_IFTYPE_STATION) | | ||||
| 		BIT(NL80211_IFTYPE_ADHOC) | | ||||
| 		BIT(NL80211_IFTYPE_MESH_POINT); | ||||
| @ -1282,7 +1279,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) | ||||
| 		ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, | ||||
| 		(sc->power_level * 2), | ||||
| 		hw_rate, | ||||
| 		info->control.rates[0].count, keyidx, 0, flags, | ||||
| 		info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, | ||||
| 		cts_rate, duration); | ||||
| 	if (ret) | ||||
| 		goto err_unmap; | ||||
| @ -1742,35 +1739,6 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void ath5k_tasklet_beacon(unsigned long data) | ||||
| { | ||||
| 	struct ath5k_softc *sc = (struct ath5k_softc *) data; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Software beacon alert--time to send a beacon. | ||||
| 	 * | ||||
| 	 * In IBSS mode we use this interrupt just to | ||||
| 	 * keep track of the next TBTT (target beacon | ||||
| 	 * transmission time) in order to detect wether | ||||
| 	 * automatic TSF updates happened. | ||||
| 	 */ | ||||
| 	if (sc->opmode == NL80211_IFTYPE_ADHOC) { | ||||
| 		/* XXX: only if VEOL suppported */ | ||||
| 		u64 tsf = ath5k_hw_get_tsf64(sc->ah); | ||||
| 		sc->nexttbtt += sc->bintval; | ||||
| 		ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | ||||
| 				"SWBA nexttbtt: %x hw_tu: %x " | ||||
| 				"TSF: %llx\n", | ||||
| 				sc->nexttbtt, | ||||
| 				TSF_TO_TU(tsf), | ||||
| 				(unsigned long long) tsf); | ||||
| 	} else { | ||||
| 		spin_lock(&sc->block); | ||||
| 		ath5k_beacon_send(sc); | ||||
| 		spin_unlock(&sc->block); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| ath5k_tasklet_rx(unsigned long data) | ||||
| { | ||||
| @ -2041,7 +2009,8 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) | ||||
| 	struct	ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ath5k_hw *ah = sc->ah; | ||||
| 	struct ath5k_desc *ds; | ||||
| 	int ret, antenna = 0; | ||||
| 	int ret = 0; | ||||
| 	u8 antenna; | ||||
| 	u32 flags; | ||||
| 
 | ||||
| 	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, | ||||
| @ -2055,23 +2024,35 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) | ||||
| 	} | ||||
| 
 | ||||
| 	ds = bf->desc; | ||||
| 	antenna = ah->ah_tx_ant; | ||||
| 
 | ||||
| 	flags = AR5K_TXDESC_NOACK; | ||||
| 	if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) { | ||||
| 		ds->ds_link = bf->daddr;	/* self-linked */ | ||||
| 		flags |= AR5K_TXDESC_VEOL; | ||||
| 		/*
 | ||||
| 		 * Let hardware handle antenna switching if txantenna is not set | ||||
| 		 */ | ||||
| 	} else { | ||||
| 	} else | ||||
| 		ds->ds_link = 0; | ||||
| 		/*
 | ||||
| 		 * Switch antenna every 4 beacons if txantenna is not set | ||||
| 		 * XXX assumes two antennas | ||||
| 		 */ | ||||
| 		if (antenna == 0) | ||||
| 			antenna = sc->bsent & 4 ? 2 : 1; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If we use multiple antennas on AP and use | ||||
| 	 * the Sectored AP scenario, switch antenna every | ||||
| 	 * 4 beacons to make sure everybody hears our AP. | ||||
| 	 * When a client tries to associate, hw will keep | ||||
| 	 * track of the tx antenna to be used for this client | ||||
| 	 * automaticaly, based on ACKed packets. | ||||
| 	 * | ||||
| 	 * Note: AP still listens and transmits RTS on the | ||||
| 	 * default antenna which is supposed to be an omni. | ||||
| 	 * | ||||
| 	 * Note2: On sectored scenarios it's possible to have | ||||
| 	 * multiple antennas (1omni -the default- and 14 sectors) | ||||
| 	 * so if we choose to actually support this mode we need | ||||
| 	 * to allow user to set how many antennas we have and tweak | ||||
| 	 * the code below to send beacons on all of them. | ||||
| 	 */ | ||||
| 	if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) | ||||
| 		antenna = sc->bsent & 4 ? 2 : 1; | ||||
| 
 | ||||
| 
 | ||||
| 	/* FIXME: If we are in g mode and rate is a CCK rate
 | ||||
| 	 * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta | ||||
| @ -2124,7 +2105,7 @@ ath5k_beacon_send(struct ath5k_softc *sc) | ||||
| 		sc->bmisscount++; | ||||
| 		ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | ||||
| 			"missed %u consecutive beacons\n", sc->bmisscount); | ||||
| 		if (sc->bmisscount > 3) {		/* NB: 3 is a guess */ | ||||
| 		if (sc->bmisscount > 10) {	/* NB: 10 is a guess */ | ||||
| 			ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | ||||
| 				"stuck beacon time (%u missed)\n", | ||||
| 				sc->bmisscount); | ||||
| @ -2145,10 +2126,12 @@ ath5k_beacon_send(struct ath5k_softc *sc) | ||||
| 	 * are still pending on the queue. | ||||
| 	 */ | ||||
| 	if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) { | ||||
| 		ATH5K_WARN(sc, "beacon queue %u didn't stop?\n", sc->bhalq); | ||||
| 		ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq); | ||||
| 		/* NB: hw still stops DMA, so proceed */ | ||||
| 	} | ||||
| 
 | ||||
| 	/* Note: Beacon buffer is updated on beacon_update when mac80211
 | ||||
| 	 * calls config_interface */ | ||||
| 	ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr); | ||||
| 	ath5k_hw_start_tx_dma(ah, sc->bhalq); | ||||
| 	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", | ||||
| @ -2305,6 +2288,35 @@ ath5k_beacon_config(struct ath5k_softc *sc) | ||||
| 	ath5k_hw_set_imr(ah, sc->imask); | ||||
| } | ||||
| 
 | ||||
| static void ath5k_tasklet_beacon(unsigned long data) | ||||
| { | ||||
| 	struct ath5k_softc *sc = (struct ath5k_softc *) data; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Software beacon alert--time to send a beacon. | ||||
| 	 * | ||||
| 	 * In IBSS mode we use this interrupt just to | ||||
| 	 * keep track of the next TBTT (target beacon | ||||
| 	 * transmission time) in order to detect wether | ||||
| 	 * automatic TSF updates happened. | ||||
| 	 */ | ||||
| 	if (sc->opmode == NL80211_IFTYPE_ADHOC) { | ||||
| 		/* XXX: only if VEOL suppported */ | ||||
| 		u64 tsf = ath5k_hw_get_tsf64(sc->ah); | ||||
| 		sc->nexttbtt += sc->bintval; | ||||
| 		ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | ||||
| 				"SWBA nexttbtt: %x hw_tu: %x " | ||||
| 				"TSF: %llx\n", | ||||
| 				sc->nexttbtt, | ||||
| 				TSF_TO_TU(tsf), | ||||
| 				(unsigned long long) tsf); | ||||
| 	} else { | ||||
| 		spin_lock(&sc->block); | ||||
| 		ath5k_beacon_send(sc); | ||||
| 		spin_unlock(&sc->block); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /********************\
 | ||||
| * Interrupt handling * | ||||
| @ -2509,7 +2521,7 @@ ath5k_intr(int irq, void *dev_id) | ||||
| 				ath5k_hw_update_mib_counters(ah, &sc->ll_stats); | ||||
| 			} | ||||
| 		} | ||||
| 	} while (ath5k_hw_is_intr_pending(ah) && counter-- > 0); | ||||
| 	} while (ath5k_hw_is_intr_pending(ah) && --counter > 0); | ||||
| 
 | ||||
| 	if (unlikely(!counter)) | ||||
| 		ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); | ||||
| @ -2751,56 +2763,47 @@ static int | ||||
| ath5k_config(struct ieee80211_hw *hw, u32 changed) | ||||
| { | ||||
| 	struct ath5k_softc *sc = hw->priv; | ||||
| 	struct ath5k_hw *ah = sc->ah; | ||||
| 	struct ieee80211_conf *conf = &hw->conf; | ||||
| 	int ret; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	mutex_lock(&sc->lock); | ||||
| 
 | ||||
| 	sc->bintval = conf->beacon_int; | ||||
| 	sc->power_level = conf->power_level; | ||||
| 
 | ||||
| 	ret = ath5k_chan_set(sc, conf->channel); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if ((changed & IEEE80211_CONF_CHANGE_POWER) && | ||||
| 	(sc->power_level != conf->power_level)) { | ||||
| 		sc->power_level = conf->power_level; | ||||
| 
 | ||||
| 		/* Half dB steps */ | ||||
| 		ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* TODO:
 | ||||
| 	 * 1) Move this on config_interface and handle each case | ||||
| 	 * separately eg. when we have only one STA vif, use | ||||
| 	 * AR5K_ANTMODE_SINGLE_AP | ||||
| 	 * | ||||
| 	 * 2) Allow the user to change antenna mode eg. when only | ||||
| 	 * one antenna is present | ||||
| 	 * | ||||
| 	 * 3) Allow the user to set default/tx antenna when possible | ||||
| 	 * | ||||
| 	 * 4) Default mode should handle 90% of the cases, together | ||||
| 	 * with fixed a/b and single AP modes we should be able to | ||||
| 	 * handle 99%. Sectored modes are extreme cases and i still | ||||
| 	 * haven't found a usage for them. If we decide to support them, | ||||
| 	 * then we must allow the user to set how many tx antennas we | ||||
| 	 * have available | ||||
| 	 */ | ||||
| 	ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); | ||||
| 
 | ||||
| 	mutex_unlock(&sc->lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||||
| 			struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct ath5k_softc *sc = hw->priv; | ||||
| 	struct ath5k_hw *ah = sc->ah; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	mutex_lock(&sc->lock); | ||||
| 	if (sc->vif != vif) { | ||||
| 		ret = -EIO; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 	if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) { | ||||
| 		/* Cache for later use during resets */ | ||||
| 		memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN); | ||||
| 		/* XXX: assoc id is set to 0 for now, mac80211 doesn't have
 | ||||
| 		 * a clean way of letting us retrieve this yet. */ | ||||
| 		ath5k_hw_set_associd(ah, ah->ah_bssid, 0); | ||||
| 		mmiowb(); | ||||
| 	} | ||||
| 	if (conf->changed & IEEE80211_IFCC_BEACON && | ||||
| 			(vif->type == NL80211_IFTYPE_ADHOC || | ||||
| 			 vif->type == NL80211_IFTYPE_MESH_POINT || | ||||
| 			 vif->type == NL80211_IFTYPE_AP)) { | ||||
| 		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||||
| 		if (!beacon) { | ||||
| 			ret = -ENOMEM; | ||||
| 			goto unlock; | ||||
| 		} | ||||
| 		ath5k_beacon_update(sc, beacon); | ||||
| 	} | ||||
| 
 | ||||
| unlock: | ||||
| 	mutex_unlock(&sc->lock); | ||||
| 	return ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #define SUPPORTED_FIF_FLAGS \ | ||||
| @ -3083,11 +3086,40 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 				    u32 changes) | ||||
| { | ||||
| 	struct ath5k_softc *sc = hw->priv; | ||||
| 	struct ath5k_hw *ah = sc->ah; | ||||
| 
 | ||||
| 	mutex_lock(&sc->lock); | ||||
| 	if (WARN_ON(sc->vif != vif)) | ||||
| 		goto unlock; | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_BSSID) { | ||||
| 		/* Cache for later use during resets */ | ||||
| 		memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 		/* XXX: assoc id is set to 0 for now, mac80211 doesn't have
 | ||||
| 		 * a clean way of letting us retrieve this yet. */ | ||||
| 		ath5k_hw_set_associd(ah, ah->ah_bssid, 0); | ||||
| 		mmiowb(); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_BEACON_INT) | ||||
| 		sc->bintval = bss_conf->beacon_int; | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_ASSOC) { | ||||
| 		mutex_lock(&sc->lock); | ||||
| 		sc->assoc = bss_conf->assoc; | ||||
| 		if (sc->opmode == NL80211_IFTYPE_STATION) | ||||
| 			set_beacon_filter(hw, sc->assoc); | ||||
| 		mutex_unlock(&sc->lock); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_BEACON && | ||||
| 	    (vif->type == NL80211_IFTYPE_ADHOC || | ||||
| 	     vif->type == NL80211_IFTYPE_MESH_POINT || | ||||
| 	     vif->type == NL80211_IFTYPE_AP)) { | ||||
| 		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||||
| 
 | ||||
| 		if (beacon) | ||||
| 			ath5k_beacon_update(sc, beacon); | ||||
| 	} | ||||
| 
 | ||||
|  unlock: | ||||
| 	mutex_unlock(&sc->lock); | ||||
| } | ||||
|  | ||||
| @ -156,6 +156,17 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) | ||||
| 		ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; | ||||
| 	} | ||||
| 
 | ||||
| 	AR5K_EEPROM_READ(AR5K_EEPROM_IS_HB63, val); | ||||
| 
 | ||||
| 	if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && val) | ||||
| 		ee->ee_is_hb63 = true; | ||||
| 	else | ||||
| 		ee->ee_is_hb63 = false; | ||||
| 
 | ||||
| 	AR5K_EEPROM_READ(AR5K_EEPROM_RFKILL, val); | ||||
| 	ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL); | ||||
| 	ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? true : false; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -197,16 +208,16 @@ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, | ||||
| 	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f; | ||||
| 	ee->ee_ant_control[mode][i++]	= val & 0x3f; | ||||
| 
 | ||||
| 	/* Get antenna modes */ | ||||
| 	ah->ah_antenna[mode][0] = | ||||
| 	/* Get antenna switch tables */ | ||||
| 	ah->ah_ant_ctl[mode][AR5K_ANT_CTL] = | ||||
| 	    (ee->ee_ant_control[mode][0] << 4); | ||||
| 	ah->ah_antenna[mode][AR5K_ANT_FIXED_A] = | ||||
| 	ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_A] = | ||||
| 	     ee->ee_ant_control[mode][1] 	| | ||||
| 	    (ee->ee_ant_control[mode][2] << 6) 	| | ||||
| 	    (ee->ee_ant_control[mode][3] << 12) | | ||||
| 	    (ee->ee_ant_control[mode][4] << 18) | | ||||
| 	    (ee->ee_ant_control[mode][5] << 24); | ||||
| 	ah->ah_antenna[mode][AR5K_ANT_FIXED_B] = | ||||
| 	ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_B] = | ||||
| 	     ee->ee_ant_control[mode][6] 	| | ||||
| 	    (ee->ee_ant_control[mode][7] << 6) 	| | ||||
| 	    (ee->ee_ant_control[mode][8] << 12) | | ||||
| @ -640,9 +651,9 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) | ||||
| static inline void | ||||
| ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) | ||||
| { | ||||
| 	const static u16 intercepts3[] = | ||||
| 	static const u16 intercepts3[] = | ||||
| 		{ 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 }; | ||||
| 	const static u16 intercepts3_2[] = | ||||
| 	static const u16 intercepts3_2[] = | ||||
| 		{ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; | ||||
| 	const u16 *ip; | ||||
| 	int i; | ||||
| @ -1694,9 +1705,40 @@ ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| ath5k_eeprom_read_spur_chans(struct ath5k_hw *ah) | ||||
| { | ||||
| 	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; | ||||
| 	u32 offset; | ||||
| 	u16 val; | ||||
| 	int ret = 0, i; | ||||
| 
 | ||||
| 	offset = AR5K_EEPROM_CTL(ee->ee_version) + | ||||
| 				AR5K_EEPROM_N_CTLS(ee->ee_version); | ||||
| 
 | ||||
| 	if (ee->ee_version < AR5K_EEPROM_VERSION_5_3) { | ||||
| 		/* No spur info for 5GHz */ | ||||
| 		ee->ee_spur_chans[0][0] = AR5K_EEPROM_NO_SPUR; | ||||
| 		/* 2 channels for 2GHz (2464/2420) */ | ||||
| 		ee->ee_spur_chans[0][1] = AR5K_EEPROM_5413_SPUR_CHAN_1; | ||||
| 		ee->ee_spur_chans[1][1] = AR5K_EEPROM_5413_SPUR_CHAN_2; | ||||
| 		ee->ee_spur_chans[2][1] = AR5K_EEPROM_NO_SPUR; | ||||
| 	} else if (ee->ee_version >= AR5K_EEPROM_VERSION_5_3) { | ||||
| 		for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { | ||||
| 			AR5K_EEPROM_READ(offset, val); | ||||
| 			ee->ee_spur_chans[i][0] = val; | ||||
| 			AR5K_EEPROM_READ(offset + AR5K_EEPROM_N_SPUR_CHANS, | ||||
| 									val); | ||||
| 			ee->ee_spur_chans[i][1] = val; | ||||
| 			offset++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialize eeprom power tables | ||||
|  * Initialize eeprom data structure | ||||
|  */ | ||||
| int | ||||
| ath5k_eeprom_init(struct ath5k_hw *ah) | ||||
| @ -1719,6 +1761,10 @@ ath5k_eeprom_init(struct ath5k_hw *ah) | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	err = ath5k_eeprom_read_spur_chans(ah); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -1754,16 +1800,3 @@ int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah) | ||||
| { | ||||
| 	u16 data; | ||||
| 
 | ||||
| 	ath5k_hw_eeprom_read(ah, AR5K_EEPROM_IS_HB63, &data); | ||||
| 
 | ||||
| 	if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && data) | ||||
| 		return true; | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -26,6 +26,13 @@ | ||||
| #define AR5K_EEPROM_MAGIC_5210		0x0000145a /* 5210 */ | ||||
| 
 | ||||
| #define	AR5K_EEPROM_IS_HB63		0x000b	/* Talon detect */ | ||||
| 
 | ||||
| #define AR5K_EEPROM_RFKILL		0x0f | ||||
| #define AR5K_EEPROM_RFKILL_GPIO_SEL	0x0000001c | ||||
| #define AR5K_EEPROM_RFKILL_GPIO_SEL_S	2 | ||||
| #define AR5K_EEPROM_RFKILL_POLARITY	0x00000002 | ||||
| #define AR5K_EEPROM_RFKILL_POLARITY_S	1 | ||||
| 
 | ||||
| #define AR5K_EEPROM_REG_DOMAIN		0x00bf	/* EEPROM regdom */ | ||||
| #define AR5K_EEPROM_CHECKSUM		0x00c0	/* EEPROM checksum */ | ||||
| #define AR5K_EEPROM_INFO_BASE		0x00c0	/* EEPROM header */ | ||||
| @ -66,11 +73,6 @@ | ||||
| #define AR5K_EEPROM_HDR_RFKILL(_v)	(((_v) >> 14) & 0x1)	/* Device has RFKill support */ | ||||
| #define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v)	(((_v) >> 15) & 0x1)	/* Disable turbo for 5Ghz */ | ||||
| 
 | ||||
| #define AR5K_EEPROM_RFKILL_GPIO_SEL	0x0000001c | ||||
| #define AR5K_EEPROM_RFKILL_GPIO_SEL_S	2 | ||||
| #define AR5K_EEPROM_RFKILL_POLARITY	0x00000002 | ||||
| #define AR5K_EEPROM_RFKILL_POLARITY_S	1 | ||||
| 
 | ||||
| /* Newer EEPROMs are using a different offset */ | ||||
| #define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \ | ||||
| 	(((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0) | ||||
| @ -211,6 +213,23 @@ | ||||
| #define AR5K_EEPROM_I_GAIN		10 | ||||
| #define AR5K_EEPROM_CCK_OFDM_DELTA	15 | ||||
| #define AR5K_EEPROM_N_IQ_CAL		2 | ||||
| /* 5GHz/2GHz */ | ||||
| enum ath5k_eeprom_freq_bands{ | ||||
| 	AR5K_EEPROM_BAND_5GHZ = 0, | ||||
| 	AR5K_EEPROM_BAND_2GHZ = 1, | ||||
| 	AR5K_EEPROM_N_FREQ_BANDS, | ||||
| }; | ||||
| /* Spur chans per freq band */ | ||||
| #define	AR5K_EEPROM_N_SPUR_CHANS	5 | ||||
| /* fbin value for chan 2464 x2 */ | ||||
| #define	AR5K_EEPROM_5413_SPUR_CHAN_1	1640 | ||||
| /* fbin value for chan 2420 x2 */ | ||||
| #define	AR5K_EEPROM_5413_SPUR_CHAN_2	1200 | ||||
| #define	AR5K_EEPROM_SPUR_CHAN_MASK	0x3FFF | ||||
| #define	AR5K_EEPROM_NO_SPUR		0x8000 | ||||
| #define	AR5K_SPUR_CHAN_WIDTH			87 | ||||
| #define	AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz	3125 | ||||
| #define	AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz	6250 | ||||
| 
 | ||||
| #define AR5K_EEPROM_READ(_o, _v) do {			\ | ||||
| 	ret = ath5k_hw_eeprom_read(ah, (_o), &(_v));	\ | ||||
| @ -221,11 +240,11 @@ | ||||
| #define AR5K_EEPROM_READ_HDR(_o, _v)					\ | ||||
| 	AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v);	\ | ||||
| 
 | ||||
| enum ath5k_ant_setting { | ||||
| 	AR5K_ANT_VARIABLE	= 0,	/* variable by programming */ | ||||
| 	AR5K_ANT_FIXED_A	= 1,	/* fixed to 11a frequencies */ | ||||
| 	AR5K_ANT_FIXED_B	= 2,	/* fixed to 11b frequencies */ | ||||
| 	AR5K_ANT_MAX		= 3, | ||||
| enum ath5k_ant_table { | ||||
| 	AR5K_ANT_CTL		= 0,	/* Idle switch table settings */ | ||||
| 	AR5K_ANT_SWTABLE_A	= 1,	/* Switch table for antenna A */ | ||||
| 	AR5K_ANT_SWTABLE_B	= 2,	/* Switch table for antenna B */ | ||||
| 	AR5K_ANT_MAX, | ||||
| }; | ||||
| 
 | ||||
| enum ath5k_ctl_mode { | ||||
| @ -369,6 +388,9 @@ struct ath5k_eeprom_info { | ||||
| 	u16	ee_version; | ||||
| 	u16	ee_header; | ||||
| 	u16	ee_ant_gain; | ||||
| 	u8	ee_rfkill_pin; | ||||
| 	bool	ee_rfkill_pol; | ||||
| 	bool	ee_is_hb63; | ||||
| 	u16	ee_misc0; | ||||
| 	u16	ee_misc1; | ||||
| 	u16	ee_misc2; | ||||
| @ -436,6 +458,10 @@ struct ath5k_eeprom_info { | ||||
| 	s8	ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES]; | ||||
| 	s8	ee_pd_gain_overlap; | ||||
| 
 | ||||
| 	/* Spur mitigation data (fbin values for spur channels) */ | ||||
| 	u16	ee_spur_chans[AR5K_EEPROM_N_SPUR_CHANS][AR5K_EEPROM_N_FREQ_BANDS]; | ||||
| 
 | ||||
| 	/* Antenna raw switch tables */ | ||||
| 	u32	ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -53,8 +53,6 @@ | ||||
| 
 | ||||
| /* Devices we match on for LED config info (typically laptops) */ | ||||
| static const struct pci_device_id ath5k_led_devices[] = { | ||||
| 	/* IBM-specific AR5212 */ | ||||
| 	{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, | ||||
| 	/* AR5211 */ | ||||
| 	{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) }, | ||||
| 	/* HP Compaq nc6xx, nc4000, nx6000 */ | ||||
| @ -69,6 +67,10 @@ static const struct pci_device_id ath5k_led_devices[] = { | ||||
| 	{ ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) }, | ||||
| 	/* Fukato Datacask Jupiter 1014a (mrb74@gmx.at) */ | ||||
| 	{ ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) }, | ||||
| 	/* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */ | ||||
| 	{ ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) }, | ||||
| 	/* IBM-specific AR5212 (all others) */ | ||||
| 	{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, | ||||
| 	{ } | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -736,8 +736,8 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | ||||
| 	/* When in AP mode zero timer0 to start TSF */ | ||||
| 	if (ah->ah_op_mode == NL80211_IFTYPE_AP) | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); | ||||
| 	else | ||||
| 		ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); | ||||
| 
 | ||||
| 	ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); | ||||
| 	ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); | ||||
| 	ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); | ||||
| 	ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); | ||||
| @ -1003,7 +1003,7 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) | ||||
| 	 * Note2: Windows driver (ndiswrapper) sets this to | ||||
| 	 *        0x00000714 instead of 0x00000007 | ||||
| 	 */ | ||||
| 	if (ah->ah_version > AR5K_AR5211) { | ||||
| 	if (ah->ah_version >= AR5K_AR5211) { | ||||
| 		ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||||
| 				AR5K_KEYTABLE_TYPE(entry)); | ||||
| 
 | ||||
|  | ||||
| @ -168,9 +168,6 @@ int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah) | ||||
|  * tx power and a Peak to Average Power Detector (PAPD) will try | ||||
|  * to measure the gain. | ||||
|  * | ||||
|  * TODO: Use propper tx power setting for the probe packet so | ||||
|  * that we don't observe a serious power drop on the receiver | ||||
|  * | ||||
|  * XXX:  How about forcing a tx packet (bypassing PCU arbitrator etc) | ||||
|  * just after we enable the probe so that we don't mess with | ||||
|  * standard traffic ? Maybe it's time to use sw interrupts and | ||||
| @ -186,7 +183,7 @@ static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) | ||||
| 
 | ||||
| 	/* Send the packet with 2dB below max power as
 | ||||
| 	 * patent doc suggest */ | ||||
| 	ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max_pwr - 4, | ||||
| 	ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4, | ||||
| 			AR5K_PHY_PAPD_PROBE_TXPOWER) | | ||||
| 			AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); | ||||
| 
 | ||||
| @ -1356,6 +1353,257 @@ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /***************************\
 | ||||
| * Spur mitigation functions * | ||||
| \***************************/ | ||||
| 
 | ||||
| bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, | ||||
| 				struct ieee80211_channel *channel) | ||||
| { | ||||
| 	u8 refclk_freq; | ||||
| 
 | ||||
| 	if ((ah->ah_radio == AR5K_RF5112) || | ||||
| 	(ah->ah_radio == AR5K_RF5413) || | ||||
| 	(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) | ||||
| 		refclk_freq = 40; | ||||
| 	else | ||||
| 		refclk_freq = 32; | ||||
| 
 | ||||
| 	if ((channel->center_freq % refclk_freq != 0) && | ||||
| 	((channel->center_freq % refclk_freq < 10) || | ||||
| 	(channel->center_freq % refclk_freq > 22))) | ||||
| 		return true; | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, | ||||
| 				struct ieee80211_channel *channel) | ||||
| { | ||||
| 	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; | ||||
| 	u32 mag_mask[4] = {0, 0, 0, 0}; | ||||
| 	u32 pilot_mask[2] = {0, 0}; | ||||
| 	/* Note: fbin values are scaled up by 2 */ | ||||
| 	u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window; | ||||
| 	s32 spur_delta_phase, spur_freq_sigma_delta; | ||||
| 	s32 spur_offset, num_symbols_x16; | ||||
| 	u8 num_symbol_offsets, i, freq_band; | ||||
| 
 | ||||
| 	/* Convert current frequency to fbin value (the same way channels
 | ||||
| 	 * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale | ||||
| 	 * up by 2 so we can compare it later */ | ||||
| 	if (channel->hw_value & CHANNEL_2GHZ) { | ||||
| 		chan_fbin = (channel->center_freq - 2300) * 10; | ||||
| 		freq_band = AR5K_EEPROM_BAND_2GHZ; | ||||
| 	} else { | ||||
| 		chan_fbin = (channel->center_freq - 4900) * 10; | ||||
| 		freq_band = AR5K_EEPROM_BAND_5GHZ; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check if any spur_chan_fbin from EEPROM is
 | ||||
| 	 * within our current channel's spur detection range */ | ||||
| 	spur_chan_fbin = AR5K_EEPROM_NO_SPUR; | ||||
| 	spur_detection_window = AR5K_SPUR_CHAN_WIDTH; | ||||
| 	/* XXX: Half/Quarter channels ?*/ | ||||
| 	if (channel->hw_value & CHANNEL_TURBO) | ||||
| 		spur_detection_window *= 2; | ||||
| 
 | ||||
| 	for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { | ||||
| 		spur_chan_fbin = ee->ee_spur_chans[i][freq_band]; | ||||
| 
 | ||||
| 		/* Note: mask cleans AR5K_EEPROM_NO_SPUR flag
 | ||||
| 		 * so it's zero if we got nothing from EEPROM */ | ||||
| 		if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) { | ||||
| 			spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((chan_fbin - spur_detection_window <= | ||||
| 		(spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) && | ||||
| 		(chan_fbin + spur_detection_window >= | ||||
| 		(spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) { | ||||
| 			spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* We need to enable spur filter for this channel */ | ||||
| 	if (spur_chan_fbin) { | ||||
| 		spur_offset = spur_chan_fbin - chan_fbin; | ||||
| 		/*
 | ||||
| 		 * Calculate deltas: | ||||
| 		 * spur_freq_sigma_delta -> spur_offset / sample_freq << 21 | ||||
| 		 * spur_delta_phase -> spur_offset / chip_freq << 11 | ||||
| 		 * Note: Both values have 100KHz resolution | ||||
| 		 */ | ||||
| 		/* XXX: Half/Quarter rate channels ? */ | ||||
| 		switch (channel->hw_value) { | ||||
| 		case CHANNEL_A: | ||||
| 			/* Both sample_freq and chip_freq are 40MHz */ | ||||
| 			spur_delta_phase = (spur_offset << 17) / 25; | ||||
| 			spur_freq_sigma_delta = (spur_delta_phase >> 10); | ||||
| 			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; | ||||
| 			break; | ||||
| 		case CHANNEL_G: | ||||
| 			/* sample_freq -> 40MHz chip_freq -> 44MHz
 | ||||
| 			 * (for b compatibility) */ | ||||
| 			spur_freq_sigma_delta = (spur_offset << 8) / 55; | ||||
| 			spur_delta_phase = (spur_offset << 17) / 25; | ||||
| 			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; | ||||
| 			break; | ||||
| 		case CHANNEL_T: | ||||
| 		case CHANNEL_TG: | ||||
| 			/* Both sample_freq and chip_freq are 80MHz */ | ||||
| 			spur_delta_phase = (spur_offset << 16) / 25; | ||||
| 			spur_freq_sigma_delta = (spur_delta_phase >> 10); | ||||
| 			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Calculate pilot and magnitude masks */ | ||||
| 
 | ||||
| 		/* Scale up spur_offset by 1000 to switch to 100HZ resolution
 | ||||
| 		 * and divide by symbol_width to find how many symbols we have | ||||
| 		 * Note: number of symbols is scaled up by 16 */ | ||||
| 		num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width; | ||||
| 
 | ||||
| 		/* Spur is on a symbol if num_symbols_x16 % 16 is zero */ | ||||
| 		if (!(num_symbols_x16 & 0xF)) | ||||
| 			/* _X_ */ | ||||
| 			num_symbol_offsets = 3; | ||||
| 		else | ||||
| 			/* _xx_ */ | ||||
| 			num_symbol_offsets = 4; | ||||
| 
 | ||||
| 		for (i = 0; i < num_symbol_offsets; i++) { | ||||
| 
 | ||||
| 			/* Calculate pilot mask */ | ||||
| 			s32 curr_sym_off = | ||||
| 				(num_symbols_x16 / 16) + i + 25; | ||||
| 
 | ||||
| 			/* Pilot magnitude mask seems to be a way to
 | ||||
| 			 * declare the boundaries for our detection | ||||
| 			 * window or something, it's 2 for the middle | ||||
| 			 * value(s) where the symbol is expected to be | ||||
| 			 * and 1 on the boundary values */ | ||||
| 			u8 plt_mag_map = | ||||
| 				(i == 0 || i == (num_symbol_offsets - 1)) | ||||
| 								? 1 : 2; | ||||
| 
 | ||||
| 			if (curr_sym_off >= 0 && curr_sym_off <= 32) { | ||||
| 				if (curr_sym_off <= 25) | ||||
| 					pilot_mask[0] |= 1 << curr_sym_off; | ||||
| 				else if (curr_sym_off >= 27) | ||||
| 					pilot_mask[0] |= 1 << (curr_sym_off - 1); | ||||
| 			} else if (curr_sym_off >= 33 && curr_sym_off <= 52) | ||||
| 				pilot_mask[1] |= 1 << (curr_sym_off - 33); | ||||
| 
 | ||||
| 			/* Calculate magnitude mask (for viterbi decoder) */ | ||||
| 			if (curr_sym_off >= -1 && curr_sym_off <= 14) | ||||
| 				mag_mask[0] |= | ||||
| 					plt_mag_map << (curr_sym_off + 1) * 2; | ||||
| 			else if (curr_sym_off >= 15 && curr_sym_off <= 30) | ||||
| 				mag_mask[1] |= | ||||
| 					plt_mag_map << (curr_sym_off - 15) * 2; | ||||
| 			else if (curr_sym_off >= 31 && curr_sym_off <= 46) | ||||
| 				mag_mask[2] |= | ||||
| 					plt_mag_map << (curr_sym_off - 31) * 2; | ||||
| 			else if (curr_sym_off >= 46 && curr_sym_off <= 53) | ||||
| 				mag_mask[3] |= | ||||
| 					plt_mag_map << (curr_sym_off - 47) * 2; | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 		/* Write settings on hw to enable spur filter */ | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, | ||||
| 					AR5K_PHY_BIN_MASK_CTL_RATE, 0xff); | ||||
| 		/* XXX: Self correlator also ? */ | ||||
| 		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, | ||||
| 					AR5K_PHY_IQ_PILOT_MASK_EN | | ||||
| 					AR5K_PHY_IQ_CHAN_MASK_EN | | ||||
| 					AR5K_PHY_IQ_SPUR_FILT_EN); | ||||
| 
 | ||||
| 		/* Set delta phase and freq sigma delta */ | ||||
| 		ath5k_hw_reg_write(ah, | ||||
| 				AR5K_REG_SM(spur_delta_phase, | ||||
| 					AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) | | ||||
| 				AR5K_REG_SM(spur_freq_sigma_delta, | ||||
| 				AR5K_PHY_TIMING_11_SPUR_FREQ_SD) | | ||||
| 				AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC, | ||||
| 				AR5K_PHY_TIMING_11); | ||||
| 
 | ||||
| 		/* Write pilot masks */ | ||||
| 		ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, | ||||
| 					AR5K_PHY_TIMING_8_PILOT_MASK_2, | ||||
| 					pilot_mask[1]); | ||||
| 
 | ||||
| 		ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, | ||||
| 					AR5K_PHY_TIMING_10_PILOT_MASK_2, | ||||
| 					pilot_mask[1]); | ||||
| 
 | ||||
| 		/* Write magnitude masks */ | ||||
| 		ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1); | ||||
| 		ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2); | ||||
| 		ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, | ||||
| 					AR5K_PHY_BIN_MASK_CTL_MASK_4, | ||||
| 					mag_mask[3]); | ||||
| 
 | ||||
| 		ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1); | ||||
| 		ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2); | ||||
| 		ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, | ||||
| 					AR5K_PHY_BIN_MASK2_4_MASK_4, | ||||
| 					mag_mask[3]); | ||||
| 
 | ||||
| 	} else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & | ||||
| 	AR5K_PHY_IQ_SPUR_FILT_EN) { | ||||
| 		/* Clean up spur mitigation settings and disable fliter */ | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, | ||||
| 					AR5K_PHY_BIN_MASK_CTL_RATE, 0); | ||||
| 		AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ, | ||||
| 					AR5K_PHY_IQ_PILOT_MASK_EN | | ||||
| 					AR5K_PHY_IQ_CHAN_MASK_EN | | ||||
| 					AR5K_PHY_IQ_SPUR_FILT_EN); | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11); | ||||
| 
 | ||||
| 		/* Clear pilot masks */ | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, | ||||
| 					AR5K_PHY_TIMING_8_PILOT_MASK_2, | ||||
| 					0); | ||||
| 
 | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, | ||||
| 					AR5K_PHY_TIMING_10_PILOT_MASK_2, | ||||
| 					0); | ||||
| 
 | ||||
| 		/* Clear magnitude masks */ | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1); | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2); | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, | ||||
| 					AR5K_PHY_BIN_MASK_CTL_MASK_4, | ||||
| 					0); | ||||
| 
 | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1); | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2); | ||||
| 		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3); | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, | ||||
| 					AR5K_PHY_BIN_MASK2_4_MASK_4, | ||||
| 					0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /********************\
 | ||||
|   Misc PHY functions | ||||
| \********************/ | ||||
| 
 | ||||
| int ath5k_hw_phy_disable(struct ath5k_hw *ah) | ||||
| { | ||||
| 	ATH5K_TRACE(ah->ah_sc); | ||||
| @ -1365,10 +1613,6 @@ int ath5k_hw_phy_disable(struct ath5k_hw *ah) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /********************\
 | ||||
|   Misc PHY functions | ||||
| \********************/ | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the PHY Chip revision | ||||
|  */ | ||||
| @ -1417,25 +1661,189 @@ u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*****************\
 | ||||
| * Antenna control * | ||||
| \*****************/ | ||||
| 
 | ||||
| void /*TODO:Boundary check*/ | ||||
| ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant) | ||||
| ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant) | ||||
| { | ||||
| 	ATH5K_TRACE(ah->ah_sc); | ||||
| 	/*Just a try M.F.*/ | ||||
| 
 | ||||
| 	if (ah->ah_version != AR5K_AR5210) | ||||
| 		ath5k_hw_reg_write(ah, ant, AR5K_DEFAULT_ANTENNA); | ||||
| 		ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA); | ||||
| } | ||||
| 
 | ||||
| unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah) | ||||
| { | ||||
| 	ATH5K_TRACE(ah->ah_sc); | ||||
| 	/*Just a try M.F.*/ | ||||
| 
 | ||||
| 	if (ah->ah_version != AR5K_AR5210) | ||||
| 		return ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA); | ||||
| 		return ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA) & 0x7; | ||||
| 
 | ||||
| 	return false; /*XXX: What do we return for 5210 ?*/ | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Enable/disable fast rx antenna diversity | ||||
|  */ | ||||
| static void | ||||
| ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) | ||||
| { | ||||
| 	switch (ee_mode) { | ||||
| 	case AR5K_EEPROM_MODE_11G: | ||||
| 		/* XXX: This is set to
 | ||||
| 		 * disabled on initvals !!! */ | ||||
| 	case AR5K_EEPROM_MODE_11A: | ||||
| 		if (enable) | ||||
| 			AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL, | ||||
| 					AR5K_PHY_AGCCTL_OFDM_DIV_DIS); | ||||
| 		else | ||||
| 			AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, | ||||
| 					AR5K_PHY_AGCCTL_OFDM_DIV_DIS); | ||||
| 		break; | ||||
| 	case AR5K_EEPROM_MODE_11B: | ||||
| 		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, | ||||
| 					AR5K_PHY_AGCCTL_OFDM_DIV_DIS); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, | ||||
| 				AR5K_PHY_RESTART_DIV_GC, 0xc); | ||||
| 
 | ||||
| 		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, | ||||
| 					AR5K_PHY_FAST_ANT_DIV_EN); | ||||
| 	} else { | ||||
| 		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, | ||||
| 				AR5K_PHY_RESTART_DIV_GC, 0x8); | ||||
| 
 | ||||
| 		AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, | ||||
| 					AR5K_PHY_FAST_ANT_DIV_EN); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Set antenna operating mode | ||||
|  */ | ||||
| void | ||||
| ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) | ||||
| { | ||||
| 	struct ieee80211_channel *channel = &ah->ah_current_channel; | ||||
| 	bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div; | ||||
| 	bool use_def_for_sg; | ||||
| 	u8 def_ant, tx_ant, ee_mode; | ||||
| 	u32 sta_id1 = 0; | ||||
| 
 | ||||
| 	def_ant = ah->ah_def_ant; | ||||
| 
 | ||||
| 	ATH5K_TRACE(ah->ah_sc); | ||||
| 
 | ||||
| 	switch (channel->hw_value & CHANNEL_MODES) { | ||||
| 	case CHANNEL_A: | ||||
| 	case CHANNEL_T: | ||||
| 	case CHANNEL_XR: | ||||
| 		ee_mode = AR5K_EEPROM_MODE_11A; | ||||
| 		break; | ||||
| 	case CHANNEL_G: | ||||
| 	case CHANNEL_TG: | ||||
| 		ee_mode = AR5K_EEPROM_MODE_11G; | ||||
| 		break; | ||||
| 	case CHANNEL_B: | ||||
| 		ee_mode = AR5K_EEPROM_MODE_11B; | ||||
| 		break; | ||||
| 	default: | ||||
| 		ATH5K_ERR(ah->ah_sc, | ||||
| 			"invalid channel: %d\n", channel->center_freq); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (ant_mode) { | ||||
| 	case AR5K_ANTMODE_DEFAULT: | ||||
| 		tx_ant = 0; | ||||
| 		use_def_for_tx = false; | ||||
| 		update_def_on_tx = false; | ||||
| 		use_def_for_rts = false; | ||||
| 		use_def_for_sg = false; | ||||
| 		fast_div = true; | ||||
| 		break; | ||||
| 	case AR5K_ANTMODE_FIXED_A: | ||||
| 		def_ant = 1; | ||||
| 		tx_ant = 0; | ||||
| 		use_def_for_tx = true; | ||||
| 		update_def_on_tx = false; | ||||
| 		use_def_for_rts = true; | ||||
| 		use_def_for_sg = true; | ||||
| 		fast_div = false; | ||||
| 		break; | ||||
| 	case AR5K_ANTMODE_FIXED_B: | ||||
| 		def_ant = 2; | ||||
| 		tx_ant = 0; | ||||
| 		use_def_for_tx = true; | ||||
| 		update_def_on_tx = false; | ||||
| 		use_def_for_rts = true; | ||||
| 		use_def_for_sg = true; | ||||
| 		fast_div = false; | ||||
| 		break; | ||||
| 	case AR5K_ANTMODE_SINGLE_AP: | ||||
| 		def_ant = 1;	/* updated on tx */ | ||||
| 		tx_ant = 0; | ||||
| 		use_def_for_tx = true; | ||||
| 		update_def_on_tx = true; | ||||
| 		use_def_for_rts = true; | ||||
| 		use_def_for_sg = true; | ||||
| 		fast_div = true; | ||||
| 		break; | ||||
| 	case AR5K_ANTMODE_SECTOR_AP: | ||||
| 		tx_ant = 1;	/* variable */ | ||||
| 		use_def_for_tx = false; | ||||
| 		update_def_on_tx = false; | ||||
| 		use_def_for_rts = true; | ||||
| 		use_def_for_sg = false; | ||||
| 		fast_div = false; | ||||
| 		break; | ||||
| 	case AR5K_ANTMODE_SECTOR_STA: | ||||
| 		tx_ant = 1;	/* variable */ | ||||
| 		use_def_for_tx = true; | ||||
| 		update_def_on_tx = false; | ||||
| 		use_def_for_rts = true; | ||||
| 		use_def_for_sg = false; | ||||
| 		fast_div = true; | ||||
| 		break; | ||||
| 	case AR5K_ANTMODE_DEBUG: | ||||
| 		def_ant = 1; | ||||
| 		tx_ant = 2; | ||||
| 		use_def_for_tx = false; | ||||
| 		update_def_on_tx = false; | ||||
| 		use_def_for_rts = false; | ||||
| 		use_def_for_sg = false; | ||||
| 		fast_div = false; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ah->ah_tx_ant = tx_ant; | ||||
| 	ah->ah_ant_mode = ant_mode; | ||||
| 
 | ||||
| 	sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0; | ||||
| 	sta_id1 |= update_def_on_tx ? AR5K_STA_ID1_DESC_ANTENNA : 0; | ||||
| 	sta_id1 |= use_def_for_rts ? AR5K_STA_ID1_RTS_DEF_ANTENNA : 0; | ||||
| 	sta_id1 |= use_def_for_sg ? AR5K_STA_ID1_SELFGEN_DEF_ANT : 0; | ||||
| 
 | ||||
| 	AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_ANTENNA_SETTINGS); | ||||
| 
 | ||||
| 	if (sta_id1) | ||||
| 		AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1); | ||||
| 
 | ||||
| 	/* Note: set diversity before default antenna
 | ||||
| 	 * because it won't work correctly */ | ||||
| 	ath5k_hw_set_fast_div(ah, ee_mode, fast_div); | ||||
| 	ath5k_hw_set_def_antenna(ah, def_ant); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /****************\
 | ||||
| * TX power setup * | ||||
| @ -1750,8 +2158,6 @@ done: | ||||
|  * Get the max edge power for this channel if | ||||
|  * we have such data from EEPROM's Conformance Test | ||||
|  * Limits (CTL), and limit max power if needed. | ||||
|  * | ||||
|  * FIXME: Only works for world regulatory domains | ||||
|  */ | ||||
| static void | ||||
| ath5k_get_max_ctl_power(struct ath5k_hw *ah, | ||||
| @ -1767,26 +2173,23 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah, | ||||
| 	u8 ctl_idx = 0xFF; | ||||
| 	u32 target = channel->center_freq; | ||||
| 
 | ||||
| 	/* Find out a CTL for our mode that's not mapped
 | ||||
| 	 * on a specific reg domain. | ||||
| 	 * | ||||
| 	 * TODO: Map our current reg domain to one of the 3 available | ||||
| 	 * reg domain ids so that we can support more CTLs. */ | ||||
| 	ctl_mode = ath_regd_get_band_ctl(&ah->ah_regulatory, channel->band); | ||||
| 
 | ||||
| 	switch (channel->hw_value & CHANNEL_MODES) { | ||||
| 	case CHANNEL_A: | ||||
| 		ctl_mode = AR5K_CTL_11A | AR5K_CTL_NO_REGDOMAIN; | ||||
| 		ctl_mode |= AR5K_CTL_11A; | ||||
| 		break; | ||||
| 	case CHANNEL_G: | ||||
| 		ctl_mode = AR5K_CTL_11G | AR5K_CTL_NO_REGDOMAIN; | ||||
| 		ctl_mode |= AR5K_CTL_11G; | ||||
| 		break; | ||||
| 	case CHANNEL_B: | ||||
| 		ctl_mode = AR5K_CTL_11B | AR5K_CTL_NO_REGDOMAIN; | ||||
| 		ctl_mode |= AR5K_CTL_11B; | ||||
| 		break; | ||||
| 	case CHANNEL_T: | ||||
| 		ctl_mode = AR5K_CTL_TURBO | AR5K_CTL_NO_REGDOMAIN; | ||||
| 		ctl_mode |= AR5K_CTL_TURBO; | ||||
| 		break; | ||||
| 	case CHANNEL_TG: | ||||
| 		ctl_mode = AR5K_CTL_TURBOG | AR5K_CTL_NO_REGDOMAIN; | ||||
| 		ctl_mode |= AR5K_CTL_TURBOG; | ||||
| 		break; | ||||
| 	case CHANNEL_XR: | ||||
| 		/* Fall through */ | ||||
| @ -2482,8 +2885,19 @@ ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, | ||||
| 		for (i = 8; i <= 15; i++) | ||||
| 			rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta; | ||||
| 
 | ||||
| 	ah->ah_txpower.txp_min_pwr = rates[7]; | ||||
| 	ah->ah_txpower.txp_max_pwr = rates[0]; | ||||
| 	/* Now that we have all rates setup use table offset to
 | ||||
| 	 * match the power range set by user with the power indices | ||||
| 	 * on PCDAC/PDADC table */ | ||||
| 	for (i = 0; i < 16; i++) { | ||||
| 		rates[i] += ah->ah_txpower.txp_offset; | ||||
| 		/* Don't get out of bounds */ | ||||
| 		if (rates[i] > 63) | ||||
| 			rates[i] = 63; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Min/max in 0.25dB units */ | ||||
| 	ah->ah_txpower.txp_min_pwr = 2 * rates[7]; | ||||
| 	ah->ah_txpower.txp_max_pwr = 2 * rates[0]; | ||||
| 	ah->ah_txpower.txp_ofdm = rates[7]; | ||||
| } | ||||
| 
 | ||||
| @ -2591,16 +3005,37 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 mode, u8 txpower) | ||||
| int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) | ||||
| { | ||||
| 	/*Just a try M.F.*/ | ||||
| 	struct ieee80211_channel *channel = &ah->ah_current_channel; | ||||
| 	u8 ee_mode; | ||||
| 
 | ||||
| 	ATH5K_TRACE(ah->ah_sc); | ||||
| 
 | ||||
| 	switch (channel->hw_value & CHANNEL_MODES) { | ||||
| 	case CHANNEL_A: | ||||
| 	case CHANNEL_T: | ||||
| 	case CHANNEL_XR: | ||||
| 		ee_mode = AR5K_EEPROM_MODE_11A; | ||||
| 		break; | ||||
| 	case CHANNEL_G: | ||||
| 	case CHANNEL_TG: | ||||
| 		ee_mode = AR5K_EEPROM_MODE_11G; | ||||
| 		break; | ||||
| 	case CHANNEL_B: | ||||
| 		ee_mode = AR5K_EEPROM_MODE_11B; | ||||
| 		break; | ||||
| 	default: | ||||
| 		ATH5K_ERR(ah->ah_sc, | ||||
| 			"invalid channel: %d\n", channel->center_freq); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER, | ||||
| 		"changing txpower to %d\n", txpower); | ||||
| 
 | ||||
| 	return ath5k_hw_txpower(ah, channel, mode, txpower); | ||||
| 	return ath5k_hw_txpower(ah, channel, ee_mode, txpower); | ||||
| } | ||||
| 
 | ||||
| #undef _ATH5K_PHY | ||||
|  | ||||
| @ -160,7 +160,8 @@ u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) | ||||
| 	if (ah->ah_version == AR5K_AR5210) | ||||
| 		return false; | ||||
| 
 | ||||
| 	pending = (AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT); | ||||
| 	pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue)); | ||||
| 	pending &= AR5K_QCU_STS_FRMPENDCNT; | ||||
| 
 | ||||
| 	/* It's possible to have no frames pending even if TXE
 | ||||
| 	 * is set. To indicate that q has not stopped return | ||||
| @ -401,14 +402,16 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) | ||||
| 			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), | ||||
| 				(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << | ||||
| 				AR5K_DCU_MISC_ARBLOCK_CTL_S) | | ||||
| 				AR5K_DCU_MISC_ARBLOCK_IGNORE | | ||||
| 				AR5K_DCU_MISC_POST_FR_BKOFF_DIS | | ||||
| 				AR5K_DCU_MISC_BCN_ENABLE); | ||||
| 			break; | ||||
| 
 | ||||
| 		case AR5K_TX_QUEUE_CAB: | ||||
| 			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), | ||||
| 				AR5K_QCU_MISC_FRSHED_DBA_GT | | ||||
| 				AR5K_QCU_MISC_FRSHED_BCN_SENT_GT | | ||||
| 				AR5K_QCU_MISC_CBREXP_DIS | | ||||
| 				AR5K_QCU_MISC_RDY_VEOL_POLICY | | ||||
| 				AR5K_QCU_MISC_CBREXP_BCN_DIS); | ||||
| 
 | ||||
| 			ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL - | ||||
|  | ||||
| @ -1148,6 +1148,11 @@ | ||||
| #define AR5K_STA_ID1_CBCIV_ENDIAN	0x40000000	/* ??? */ | ||||
| #define AR5K_STA_ID1_KEYSRCH_MCAST	0x80000000	/* Do key cache search for mcast frames */ | ||||
| 
 | ||||
| #define	AR5K_STA_ID1_ANTENNA_SETTINGS	(AR5K_STA_ID1_DEFAULT_ANTENNA | \ | ||||
| 					AR5K_STA_ID1_DESC_ANTENNA | \ | ||||
| 					AR5K_STA_ID1_RTS_DEF_ANTENNA | \ | ||||
| 					AR5K_STA_ID1_SELFGEN_DEF_ANT) | ||||
| 
 | ||||
| /*
 | ||||
|  * First BSSID register (MAC address, lower 32bits) | ||||
|  */ | ||||
| @ -2028,7 +2033,9 @@ | ||||
| #define	AR5K_PHY_AGCCTL			0x9860			/* Register address */ | ||||
| #define	AR5K_PHY_AGCCTL_CAL		0x00000001	/* Enable PHY calibration */ | ||||
| #define	AR5K_PHY_AGCCTL_NF		0x00000002	/* Enable Noise Floor calibration */ | ||||
| #define	AR5K_PHY_AGCCTL_OFDM_DIV_DIS	0x00000008	/* Disable antenna diversity on OFDM modes */ | ||||
| #define	AR5K_PHY_AGCCTL_NF_EN		0x00008000	/* Enable nf calibration to happen (?) */ | ||||
| #define	AR5K_PHY_AGCTL_FLTR_CAL		0x00010000	/* Allow filter calibration (?) */ | ||||
| #define	AR5K_PHY_AGCCTL_NF_NOUPDATE	0x00020000	/* Don't update nf automaticaly */ | ||||
| 
 | ||||
| /*
 | ||||
| @ -2528,7 +2535,7 @@ | ||||
|  * PHY CCK Cross-correlator Barker RSSI threshold register [5212+] | ||||
|  */ | ||||
| #define AR5K_PHY_CCK_CROSSCORR			0xa208 | ||||
| #define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR	0x0000000f | ||||
| #define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR	0x0000003f | ||||
| #define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR_S	0 | ||||
| 
 | ||||
| /* Same address is used for antenna diversity activation */ | ||||
|  | ||||
| @ -507,7 +507,7 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) | ||||
| 
 | ||||
| 		if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)) | ||||
| 			scal = AR5K_PHY_SCAL_32MHZ_2417; | ||||
| 		else if (ath5k_eeprom_is_hb63(ah)) | ||||
| 		else if (ee->ee_is_hb63) | ||||
| 			scal = AR5K_PHY_SCAL_32MHZ_HB63; | ||||
| 		else | ||||
| 			scal = AR5K_PHY_SCAL_32MHZ; | ||||
| @ -536,26 +536,6 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable) | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, | ||||
| 				struct ieee80211_channel *channel) | ||||
| { | ||||
| 	u8 refclk_freq; | ||||
| 
 | ||||
| 	if ((ah->ah_radio == AR5K_RF5112) || | ||||
| 	(ah->ah_radio == AR5K_RF5413) || | ||||
| 	(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) | ||||
| 		refclk_freq = 40; | ||||
| 	else | ||||
| 		refclk_freq = 32; | ||||
| 
 | ||||
| 	if ((channel->center_freq % refclk_freq != 0) && | ||||
| 	((channel->center_freq % refclk_freq < 10) || | ||||
| 	(channel->center_freq % refclk_freq > 22))) | ||||
| 		return true; | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
| 
 | ||||
| /* TODO: Half/Quarter rate */ | ||||
| static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, | ||||
| 				struct ieee80211_channel *channel) | ||||
| @ -598,9 +578,10 @@ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah, | ||||
| 	/* Set DAC/ADC delays */ | ||||
| 	if (ah->ah_version == AR5K_AR5212) { | ||||
| 		u32 scal; | ||||
| 		struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; | ||||
| 		if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)) | ||||
| 			scal = AR5K_PHY_SCAL_32MHZ_2417; | ||||
| 		else if (ath5k_eeprom_is_hb63(ah)) | ||||
| 		else if (ee->ee_is_hb63) | ||||
| 			scal = AR5K_PHY_SCAL_32MHZ_HB63; | ||||
| 		else | ||||
| 			scal = AR5K_PHY_SCAL_32MHZ; | ||||
| @ -697,13 +678,13 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah, | ||||
| 	/* Set antenna idle switch table */ | ||||
| 	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL, | ||||
| 			AR5K_PHY_ANT_CTL_SWTABLE_IDLE, | ||||
| 			(ah->ah_antenna[ee_mode][0] | | ||||
| 			(ah->ah_ant_ctl[ee_mode][0] | | ||||
| 			AR5K_PHY_ANT_CTL_TXRX_EN)); | ||||
| 
 | ||||
| 	/* Set antenna switch table */ | ||||
| 	ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[0]], | ||||
| 	/* Set antenna switch tables */ | ||||
| 	ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant[0]], | ||||
| 		AR5K_PHY_ANT_SWITCH_TABLE_0); | ||||
| 	ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[1]], | ||||
| 	ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant[1]], | ||||
| 		AR5K_PHY_ANT_SWITCH_TABLE_1); | ||||
| 
 | ||||
| 	/* Noise floor threshold */ | ||||
| @ -997,10 +978,10 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, | ||||
| 		ath5k_hw_tweak_initval_settings(ah, channel); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Set TX power (FIXME) | ||||
| 		 * Set TX power | ||||
| 		 */ | ||||
| 		ret = ath5k_hw_txpower(ah, channel, ee_mode, | ||||
| 					AR5K_TUNE_DEFAULT_TXPOWER); | ||||
| 					ah->ah_txpower.txp_max_pwr / 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| @ -1023,9 +1004,22 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, | ||||
| 		/* Write OFDM timings on 5212*/ | ||||
| 		if (ah->ah_version == AR5K_AR5212 && | ||||
| 			channel->hw_value & CHANNEL_OFDM) { | ||||
| 			struct ath5k_eeprom_info *ee = | ||||
| 					&ah->ah_capabilities.cap_eeprom; | ||||
| 
 | ||||
| 			ret = ath5k_hw_write_ofdm_timings(ah, channel); | ||||
| 			if (ret) | ||||
| 				return ret; | ||||
| 
 | ||||
| 			/* Note: According to docs we can have a newer
 | ||||
| 			 * EEPROM on old hardware, so we need to verify | ||||
| 			 * that our hardware is new enough to have spur | ||||
| 			 * mitigation registers (delta phase etc) */ | ||||
| 			if (ah->ah_mac_srev >= AR5K_SREV_AR5424 || | ||||
| 			(ah->ah_mac_srev >= AR5K_SREV_AR5424 && | ||||
| 			ee->ee_version >= AR5K_EEPROM_VERSION_5_3)) | ||||
| 				ath5k_hw_set_spur_mitigation_filter(ah, | ||||
| 								channel); | ||||
| 		} | ||||
| 
 | ||||
| 		/*Enable/disable 802.11b mode on 5111
 | ||||
| @ -1041,17 +1035,15 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * In case a fixed antenna was set as default | ||||
| 		 * write the same settings on both AR5K_PHY_ANT_SWITCH_TABLE | ||||
| 		 * registers. | ||||
| 		 * use the same switch table twice. | ||||
| 		 */ | ||||
| 		if (s_ant != 0) { | ||||
| 			if (s_ant == AR5K_ANT_FIXED_A) /* 1 - Main */ | ||||
| 				ant[0] = ant[1] = AR5K_ANT_FIXED_A; | ||||
| 			else	/* 2 - Aux */ | ||||
| 				ant[0] = ant[1] = AR5K_ANT_FIXED_B; | ||||
| 		} else { | ||||
| 			ant[0] = AR5K_ANT_FIXED_A; | ||||
| 			ant[1] = AR5K_ANT_FIXED_B; | ||||
| 		if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A) | ||||
| 				ant[0] = ant[1] = AR5K_ANT_SWTABLE_A; | ||||
| 		else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B) | ||||
| 				ant[0] = ant[1] = AR5K_ANT_SWTABLE_B; | ||||
| 		else { | ||||
| 			ant[0] = AR5K_ANT_SWTABLE_A; | ||||
| 			ant[1] = AR5K_ANT_SWTABLE_B; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Commit values from EEPROM */ | ||||
| @ -1259,6 +1251,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, | ||||
| 	 */ | ||||
| 	ath5k_hw_noise_floor_calibration(ah, channel->center_freq); | ||||
| 
 | ||||
| 	/* Restore antenna mode */ | ||||
| 	ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Configure QCUs/DCUs | ||||
|  | ||||
| @ -576,8 +576,8 @@ struct ath_softc { | ||||
| 	struct ath_tx tx; | ||||
| 	struct ath_beacon beacon; | ||||
| 	struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; | ||||
| 	struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX]; | ||||
| 	struct ath_rate_table *cur_rate_table; | ||||
| 	const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX]; | ||||
| 	const struct ath_rate_table *cur_rate_table; | ||||
| 	struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; | ||||
| 
 | ||||
| 	struct ath_led radio_led; | ||||
| @ -590,6 +590,8 @@ struct ath_softc { | ||||
| 	int led_on_cnt; | ||||
| 	int led_off_cnt; | ||||
| 
 | ||||
| 	int beacon_interval; | ||||
| 
 | ||||
| 	struct ath_rfkill rf_kill; | ||||
| 	struct ath_ani ani; | ||||
| 	struct ath9k_node_stats nodestats; | ||||
| @ -695,36 +697,7 @@ void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, | ||||
| bool ath9k_wiphy_scanning(struct ath_softc *sc); | ||||
| void ath9k_wiphy_work(struct work_struct *work); | ||||
| 
 | ||||
| /*
 | ||||
|  * Read and write, they both share the same lock. We do this to serialize | ||||
|  * reads and writes on Atheros 802.11n PCI devices only. This is required | ||||
|  * as the FIFO on these devices can only accept sanely 2 requests. After | ||||
|  * that the device goes bananas. Serializing the reads/writes prevents this | ||||
|  * from happening. | ||||
|  */ | ||||
| 
 | ||||
| static inline void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val) | ||||
| { | ||||
| 	if (ah->config.serialize_regmode == SER_REG_MODE_ON) { | ||||
| 		unsigned long flags; | ||||
| 		spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 		iowrite32(val, ah->ah_sc->mem + reg_offset); | ||||
| 		spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 	} else | ||||
| 		iowrite32(val, ah->ah_sc->mem + reg_offset); | ||||
| } | ||||
| 
 | ||||
| static inline unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset) | ||||
| { | ||||
| 	u32 val; | ||||
| 	if (ah->config.serialize_regmode == SER_REG_MODE_ON) { | ||||
| 		unsigned long flags; | ||||
| 		spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 		val = ioread32(ah->ah_sc->mem + reg_offset); | ||||
| 		spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 	} else | ||||
| 		val = ioread32(ah->ah_sc->mem + reg_offset); | ||||
| 	return val; | ||||
| } | ||||
| void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val); | ||||
| unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset); | ||||
| 
 | ||||
| #endif /* ATH9K_H */ | ||||
|  | ||||
| @ -63,7 +63,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, | ||||
| 	struct ath_hw *ah = sc->sc_ah; | ||||
| 	struct ath_desc *ds; | ||||
| 	struct ath9k_11n_rate_series series[4]; | ||||
| 	struct ath_rate_table *rt; | ||||
| 	const struct ath_rate_table *rt; | ||||
| 	int flags, antenna, ctsrate = 0, ctsduration = 0; | ||||
| 	u8 rate; | ||||
| 
 | ||||
| @ -320,8 +320,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) | ||||
| 		u64 tsfadjust; | ||||
| 		int intval; | ||||
| 
 | ||||
| 		intval = sc->hw->conf.beacon_int ? | ||||
| 			sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; | ||||
| 		intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Calculate the TSF offset for this beacon slot, i.e., the | ||||
| @ -431,8 +430,7 @@ void ath_beacon_tasklet(unsigned long data) | ||||
| 	 * on the tsf to safeguard against missing an swba. | ||||
| 	 */ | ||||
| 
 | ||||
| 	intval = sc->hw->conf.beacon_int ? | ||||
| 		sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; | ||||
| 	intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; | ||||
| 
 | ||||
| 	tsf = ath9k_hw_gettsf64(ah); | ||||
| 	tsftu = TSF_TO_TU(tsf>>32, tsf); | ||||
| @ -711,8 +709,7 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) | ||||
| 	/* Setup the beacon configuration parameters */ | ||||
| 
 | ||||
| 	memset(&conf, 0, sizeof(struct ath_beacon_config)); | ||||
| 	conf.beacon_interval = sc->hw->conf.beacon_int ? | ||||
| 		sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; | ||||
| 	conf.beacon_interval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; | ||||
| 	conf.listen_interval = 1; | ||||
| 	conf.dtim_period = conf.beacon_interval; | ||||
| 	conf.dtim_count = 1; | ||||
|  | ||||
| @ -863,7 +863,7 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, | ||||
| 	} | ||||
| 
 | ||||
| 	if (longcal) { | ||||
| 		if (AR_SREV_9285(ah) && AR_SREV_9285_11_OR_LATER(ah)) | ||||
| 		if (AR_SREV_9285_11_OR_LATER(ah)) | ||||
| 			ath9k_hw_9285_pa_cal(ah); | ||||
| 
 | ||||
| 		if (OLC_FOR_AR9280_20_LATER) | ||||
| @ -917,7 +917,7 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) | ||||
| 
 | ||||
| bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) | ||||
| { | ||||
| 	if (AR_SREV_9285(ah) && AR_SREV_9285_12_OR_LATER(ah)) { | ||||
| 	if (AR_SREV_9285_12_OR_LATER(ah)) { | ||||
| 		if (!ar9285_clc(ah, chan)) | ||||
| 			return false; | ||||
| 	} else { | ||||
| @ -947,7 +947,7 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) | ||||
| 	} | ||||
| 
 | ||||
| 	/* Do PA Calibration */ | ||||
| 	if (AR_SREV_9285(ah) && AR_SREV_9285_11_OR_LATER(ah)) | ||||
| 	if (AR_SREV_9285_11_OR_LATER(ah)) | ||||
| 		ath9k_hw_9285_pa_cal(ah); | ||||
| 
 | ||||
| 	/* Do NF Calibration after DC offset and other calibrations */ | ||||
|  | ||||
| @ -84,6 +84,38 @@ static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) | ||||
| 		return ath9k_hw_mac_clks(ah, usecs); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Read and write, they both share the same lock. We do this to serialize | ||||
|  * reads and writes on Atheros 802.11n PCI devices only. This is required | ||||
|  * as the FIFO on these devices can only accept sanely 2 requests. After | ||||
|  * that the device goes bananas. Serializing the reads/writes prevents this | ||||
|  * from happening. | ||||
|  */ | ||||
| 
 | ||||
| void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val) | ||||
| { | ||||
| 	if (ah->config.serialize_regmode == SER_REG_MODE_ON) { | ||||
| 		unsigned long flags; | ||||
| 		spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 		iowrite32(val, ah->ah_sc->mem + reg_offset); | ||||
| 		spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 	} else | ||||
| 		iowrite32(val, ah->ah_sc->mem + reg_offset); | ||||
| } | ||||
| 
 | ||||
| unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset) | ||||
| { | ||||
| 	u32 val; | ||||
| 	if (ah->config.serialize_regmode == SER_REG_MODE_ON) { | ||||
| 		unsigned long flags; | ||||
| 		spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 		val = ioread32(ah->ah_sc->mem + reg_offset); | ||||
| 		spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); | ||||
| 	} else | ||||
| 		val = ioread32(ah->ah_sc->mem + reg_offset); | ||||
| 	return val; | ||||
| } | ||||
| 
 | ||||
| bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) | ||||
| { | ||||
| 	int i; | ||||
| @ -136,7 +168,7 @@ bool ath9k_get_channel_edges(struct ath_hw *ah, | ||||
| } | ||||
| 
 | ||||
| u16 ath9k_hw_computetxtime(struct ath_hw *ah, | ||||
| 			   struct ath_rate_table *rates, | ||||
| 			   const struct ath_rate_table *rates, | ||||
| 			   u32 frameLen, u16 rateix, | ||||
| 			   bool shortPreamble) | ||||
| { | ||||
| @ -1313,8 +1345,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, | ||||
| 	if (AR_SREV_9280(ah)) | ||||
| 		REG_WRITE_ARRAY(&ah->iniModesRxGain, modesIndex, regWrites); | ||||
| 
 | ||||
| 	if (AR_SREV_9280(ah) || (AR_SREV_9285(ah) && | ||||
| 	    AR_SREV_9285_12_OR_LATER(ah))) | ||||
| 	if (AR_SREV_9280(ah) || AR_SREV_9285_12_OR_LATER(ah)) | ||||
| 		REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); | ||||
| 
 | ||||
| 	for (i = 0; i < ah->iniCommon.ia_rows; i++) { | ||||
|  | ||||
| @ -580,7 +580,8 @@ bool ath9k_hw_setantennaswitch(struct ath_hw *ah, | ||||
| bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); | ||||
| u32 ath9k_hw_reverse_bits(u32 val, u32 n); | ||||
| bool ath9k_get_channel_edges(struct ath_hw *ah, u16 flags, u16 *low, u16 *high); | ||||
| u16 ath9k_hw_computetxtime(struct ath_hw *ah, struct ath_rate_table *rates, | ||||
| u16 ath9k_hw_computetxtime(struct ath_hw *ah, | ||||
| 			   const struct ath_rate_table *rates, | ||||
| 			   u32 frameLen, u16 rateix, bool shortPreamble); | ||||
| void ath9k_hw_get_channel_centers(struct ath_hw *ah, | ||||
| 				  struct ath9k_channel *chan, | ||||
|  | ||||
| @ -189,7 +189,7 @@ static u8 parse_mpdudensity(u8 mpdudensity) | ||||
| 
 | ||||
| static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) | ||||
| { | ||||
| 	struct ath_rate_table *rate_table = NULL; | ||||
| 	const struct ath_rate_table *rate_table = NULL; | ||||
| 	struct ieee80211_supported_band *sband; | ||||
| 	struct ieee80211_rate *rate; | ||||
| 	int i, maxrates; | ||||
| @ -2358,114 +2358,6 @@ skip_chan_change: | ||||
| 	if (changed & IEEE80211_CONF_CHANGE_POWER) | ||||
| 		sc->config.txpowlimit = 2 * conf->power_level; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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 & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) | ||||
| 		sc->sc_flags |= SC_OP_TSF_RESET; | ||||
| 
 | ||||
| 	mutex_unlock(&sc->mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ath9k_config_interface(struct ieee80211_hw *hw, | ||||
| 				  struct ieee80211_vif *vif, | ||||
| 				  struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct ath_wiphy *aphy = hw->priv; | ||||
| 	struct ath_softc *sc = aphy->sc; | ||||
| 	struct ath_hw *ah = sc->sc_ah; | ||||
| 	struct ath_vif *avp = (void *)vif->drv_priv; | ||||
| 	u32 rfilt = 0; | ||||
| 	int error, i; | ||||
| 
 | ||||
| 	mutex_lock(&sc->mutex); | ||||
| 
 | ||||
| 	/* TODO: Need to decide which hw opmode to use for multi-interface
 | ||||
| 	 * cases */ | ||||
| 	if (vif->type == NL80211_IFTYPE_AP && | ||||
| 	    ah->opmode != NL80211_IFTYPE_AP) { | ||||
| 		ah->opmode = NL80211_IFTYPE_STATION; | ||||
| 		ath9k_hw_setopmode(ah); | ||||
| 		memcpy(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN); | ||||
| 		sc->curaid = 0; | ||||
| 		ath9k_hw_write_associd(sc); | ||||
| 		/* Request full reset to get hw opmode changed properly */ | ||||
| 		sc->sc_flags |= SC_OP_FULL_RESET; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((conf->changed & IEEE80211_IFCC_BSSID) && | ||||
| 	    !is_zero_ether_addr(conf->bssid)) { | ||||
| 		switch (vif->type) { | ||||
| 		case NL80211_IFTYPE_STATION: | ||||
| 		case NL80211_IFTYPE_ADHOC: | ||||
| 		case NL80211_IFTYPE_MESH_POINT: | ||||
| 			/* Set BSSID */ | ||||
| 			memcpy(sc->curbssid, conf->bssid, ETH_ALEN); | ||||
| 			memcpy(avp->bssid, conf->bssid, ETH_ALEN); | ||||
| 			sc->curaid = 0; | ||||
| 			ath9k_hw_write_associd(sc); | ||||
| 
 | ||||
| 			/* Set aggregation protection mode parameters */ | ||||
| 			sc->config.ath_aggr_prot = 0; | ||||
| 
 | ||||
| 			DPRINTF(sc, ATH_DBG_CONFIG, | ||||
| 				"RX filter 0x%x bssid %pM aid 0x%x\n", | ||||
| 				rfilt, sc->curbssid, sc->curaid); | ||||
| 
 | ||||
| 			/* need to reconfigure the beacon */ | ||||
| 			sc->sc_flags &= ~SC_OP_BEACONS ; | ||||
| 
 | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ((vif->type == NL80211_IFTYPE_ADHOC) || | ||||
| 	    (vif->type == NL80211_IFTYPE_AP) || | ||||
| 	    (vif->type == NL80211_IFTYPE_MESH_POINT)) { | ||||
| 		if ((conf->changed & IEEE80211_IFCC_BEACON) || | ||||
| 		    (conf->changed & IEEE80211_IFCC_BEACON_ENABLED && | ||||
| 		     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. | ||||
| 			 */ | ||||
| 			ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); | ||||
| 
 | ||||
| 			error = ath_beacon_alloc(aphy, vif); | ||||
| 			if (error != 0) { | ||||
| 				mutex_unlock(&sc->mutex); | ||||
| 				return error; | ||||
| 			} | ||||
| 
 | ||||
| 			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, | ||||
| 						   sc->curbssid); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Only legacy IBSS for now */ | ||||
| 	if (vif->type == NL80211_IFTYPE_ADHOC) | ||||
| 		ath_update_chainmask(sc, 0); | ||||
| 
 | ||||
| 	mutex_unlock(&sc->mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| @ -2607,9 +2499,92 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | ||||
| { | ||||
| 	struct ath_wiphy *aphy = hw->priv; | ||||
| 	struct ath_softc *sc = aphy->sc; | ||||
| 	struct ath_hw *ah = sc->sc_ah; | ||||
| 	struct ath_vif *avp = (void *)vif->drv_priv; | ||||
| 	u32 rfilt = 0; | ||||
| 	int error, i; | ||||
| 
 | ||||
| 	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(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN); | ||||
| 		sc->curaid = 0; | ||||
| 		ath9k_hw_write_associd(sc); | ||||
| 		/* Request full reset to get hw opmode changed properly */ | ||||
| 		sc->sc_flags |= SC_OP_FULL_RESET; | ||||
| 	} | ||||
| 
 | ||||
| 	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(sc->curbssid, bss_conf->bssid, ETH_ALEN); | ||||
| 			memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 			sc->curaid = 0; | ||||
| 			ath9k_hw_write_associd(sc); | ||||
| 
 | ||||
| 			/* Set aggregation protection mode parameters */ | ||||
| 			sc->config.ath_aggr_prot = 0; | ||||
| 
 | ||||
| 			DPRINTF(sc, ATH_DBG_CONFIG, | ||||
| 				"RX filter 0x%x bssid %pM aid 0x%x\n", | ||||
| 				rfilt, sc->curbssid, sc->curaid); | ||||
| 
 | ||||
| 			/* need to reconfigure the beacon */ | ||||
| 			sc->sc_flags &= ~SC_OP_BEACONS ; | ||||
| 
 | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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. | ||||
| 			 */ | ||||
| 			ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); | ||||
| 
 | ||||
| 			error = ath_beacon_alloc(aphy, vif); | ||||
| 			if (!error) | ||||
| 				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, | ||||
| 						   sc->curbssid); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Only legacy IBSS for now */ | ||||
| 	if (vif->type == NL80211_IFTYPE_ADHOC) | ||||
| 		ath_update_chainmask(sc, 0); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ERP_PREAMBLE) { | ||||
| 		DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", | ||||
| 			bss_conf->use_short_preamble); | ||||
| @ -2635,6 +2610,18 @@ 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); | ||||
| } | ||||
| 
 | ||||
| @ -2755,7 +2742,6 @@ struct ieee80211_ops ath9k_ops = { | ||||
| 	.add_interface 	    = ath9k_add_interface, | ||||
| 	.remove_interface   = ath9k_remove_interface, | ||||
| 	.config 	    = ath9k_config, | ||||
| 	.config_interface   = ath9k_config_interface, | ||||
| 	.configure_filter   = ath9k_configure_filter, | ||||
| 	.sta_notify         = ath9k_sta_notify, | ||||
| 	.conf_tx 	    = ath9k_conf_tx, | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| 
 | ||||
| #include "ath9k.h" | ||||
| 
 | ||||
| static struct ath_rate_table ar5416_11na_ratetable = { | ||||
| static const struct ath_rate_table ar5416_11na_ratetable = { | ||||
| 	42, | ||||
| 	{ | ||||
| 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ | ||||
| @ -155,7 +155,7 @@ static struct ath_rate_table ar5416_11na_ratetable = { | ||||
| /* 4ms frame limit not used for NG mode.  The values filled
 | ||||
|  * for HT are the 64K max aggregate limit */ | ||||
| 
 | ||||
| static struct ath_rate_table ar5416_11ng_ratetable = { | ||||
| static const struct ath_rate_table ar5416_11ng_ratetable = { | ||||
| 	46, | ||||
| 	{ | ||||
| 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ | ||||
| @ -302,7 +302,7 @@ static struct ath_rate_table ar5416_11ng_ratetable = { | ||||
| 	WLAN_RC_HT_FLAG,  /* Phy rates allowed initially */ | ||||
| }; | ||||
| 
 | ||||
| static struct ath_rate_table ar5416_11a_ratetable = { | ||||
| static const struct ath_rate_table ar5416_11a_ratetable = { | ||||
| 	8, | ||||
| 	{ | ||||
| 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ | ||||
| @ -335,7 +335,7 @@ static struct ath_rate_table ar5416_11a_ratetable = { | ||||
| 	0,   /* Phy rates allowed initially */ | ||||
| }; | ||||
| 
 | ||||
| static struct ath_rate_table ar5416_11g_ratetable = { | ||||
| static const struct ath_rate_table ar5416_11g_ratetable = { | ||||
| 	12, | ||||
| 	{ | ||||
| 		{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ | ||||
| @ -380,7 +380,7 @@ static struct ath_rate_table ar5416_11g_ratetable = { | ||||
| 	0,   /* Phy rates allowed initially */ | ||||
| }; | ||||
| 
 | ||||
| static struct ath_rate_table ar5416_11b_ratetable = { | ||||
| static const struct ath_rate_table ar5416_11b_ratetable = { | ||||
| 	4, | ||||
| 	{ | ||||
| 		{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ | ||||
| @ -420,7 +420,7 @@ static inline int8_t median(int8_t a, int8_t b, int8_t c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void ath_rc_sort_validrates(struct ath_rate_table *rate_table, | ||||
| static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table, | ||||
| 				   struct ath_rate_priv *ath_rc_priv) | ||||
| { | ||||
| 	u8 i, j, idx, idx_next; | ||||
| @ -461,10 +461,11 @@ static inline int ath_rc_isvalid_txmask(struct ath_rate_priv *ath_rc_priv, | ||||
| 	return ath_rc_priv->valid_rate_index[index]; | ||||
| } | ||||
| 
 | ||||
| static inline int ath_rc_get_nextvalid_txrate(struct ath_rate_table *rate_table, | ||||
| 					      struct ath_rate_priv *ath_rc_priv, | ||||
| 					      u8 cur_valid_txrate, | ||||
| 					      u8 *next_idx) | ||||
| static inline | ||||
| int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, | ||||
| 				struct ath_rate_priv *ath_rc_priv, | ||||
| 				u8 cur_valid_txrate, | ||||
| 				u8 *next_idx) | ||||
| { | ||||
| 	u8 i; | ||||
| 
 | ||||
| @ -500,7 +501,7 @@ static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) | ||||
| } | ||||
| 
 | ||||
| static inline int | ||||
| ath_rc_get_nextlowervalid_txrate(struct ath_rate_table *rate_table, | ||||
| ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table, | ||||
| 				 struct ath_rate_priv *ath_rc_priv, | ||||
| 				 u8 cur_valid_txrate, u8 *next_idx) | ||||
| { | ||||
| @ -517,7 +518,7 @@ ath_rc_get_nextlowervalid_txrate(struct ath_rate_table *rate_table, | ||||
| } | ||||
| 
 | ||||
| static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv, | ||||
| 				 struct ath_rate_table *rate_table, | ||||
| 				 const struct ath_rate_table *rate_table, | ||||
| 				 u32 capflag) | ||||
| { | ||||
| 	u8 i, hi = 0; | ||||
| @ -547,7 +548,7 @@ static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv, | ||||
| } | ||||
| 
 | ||||
| static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, | ||||
| 				struct ath_rate_table *rate_table, | ||||
| 				const struct ath_rate_table *rate_table, | ||||
| 				struct ath_rateset *rateset, | ||||
| 				u32 capflag) | ||||
| { | ||||
| @ -592,7 +593,7 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, | ||||
| } | ||||
| 
 | ||||
| static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, | ||||
| 				  struct ath_rate_table *rate_table, | ||||
| 				  const struct ath_rate_table *rate_table, | ||||
| 				  u8 *mcs_set, u32 capflag) | ||||
| { | ||||
| 	struct ath_rateset *rateset = (struct ath_rateset *)mcs_set; | ||||
| @ -630,7 +631,7 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, | ||||
| 
 | ||||
| static u8 ath_rc_ratefind_ht(struct ath_softc *sc, | ||||
| 			     struct ath_rate_priv *ath_rc_priv, | ||||
| 			     struct ath_rate_table *rate_table, | ||||
| 			     const struct ath_rate_table *rate_table, | ||||
| 			     int *is_probing) | ||||
| { | ||||
| 	u32 dt, best_thruput, this_thruput, now_msec; | ||||
| @ -748,7 +749,7 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc, | ||||
| 	return rate; | ||||
| } | ||||
| 
 | ||||
| static void ath_rc_rate_set_series(struct ath_rate_table *rate_table, | ||||
| static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table, | ||||
| 				   struct ieee80211_tx_rate *rate, | ||||
| 				   struct ieee80211_tx_rate_control *txrc, | ||||
| 				   u8 tries, u8 rix, int rtsctsenable) | ||||
| @ -769,7 +770,7 @@ static void ath_rc_rate_set_series(struct ath_rate_table *rate_table, | ||||
| } | ||||
| 
 | ||||
| static void ath_rc_rate_set_rtscts(struct ath_softc *sc, | ||||
| 				   struct ath_rate_table *rate_table, | ||||
| 				   const struct ath_rate_table *rate_table, | ||||
| 				   struct ieee80211_tx_info *tx_info) | ||||
| { | ||||
| 	struct ieee80211_tx_rate *rates = tx_info->control.rates; | ||||
| @ -807,7 +808,7 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc, | ||||
| 
 | ||||
| static u8 ath_rc_rate_getidx(struct ath_softc *sc, | ||||
| 			     struct ath_rate_priv *ath_rc_priv, | ||||
| 			     struct ath_rate_table *rate_table, | ||||
| 			     const struct ath_rate_table *rate_table, | ||||
| 			     u8 rix, u16 stepdown, | ||||
| 			     u16 min_rate) | ||||
| { | ||||
| @ -838,7 +839,7 @@ static void ath_rc_ratefind(struct ath_softc *sc, | ||||
| 			    struct ath_rate_priv *ath_rc_priv, | ||||
| 			    struct ieee80211_tx_rate_control *txrc) | ||||
| { | ||||
| 	struct ath_rate_table *rate_table; | ||||
| 	const struct ath_rate_table *rate_table; | ||||
| 	struct sk_buff *skb = txrc->skb; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_tx_rate *rates = tx_info->control.rates; | ||||
| @ -937,7 +938,7 @@ static void ath_rc_ratefind(struct ath_softc *sc, | ||||
| } | ||||
| 
 | ||||
| static bool ath_rc_update_per(struct ath_softc *sc, | ||||
| 			      struct ath_rate_table *rate_table, | ||||
| 			      const struct ath_rate_table *rate_table, | ||||
| 			      struct ath_rate_priv *ath_rc_priv, | ||||
| 			      struct ath_tx_info_priv *tx_info_priv, | ||||
| 			      int tx_rate, int xretries, int retries, | ||||
| @ -1142,7 +1143,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, | ||||
| 	int rate; | ||||
| 	u8 last_per; | ||||
| 	bool state_change = false; | ||||
| 	struct ath_rate_table *rate_table = sc->cur_rate_table; | ||||
| 	const struct ath_rate_table *rate_table = sc->cur_rate_table; | ||||
| 	int size = ath_rc_priv->rate_table_size; | ||||
| 
 | ||||
| 	if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) | ||||
| @ -1276,7 +1277,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, | ||||
| #undef CHK_RSSI | ||||
| } | ||||
| 
 | ||||
| static int ath_rc_get_rateindex(struct ath_rate_table *rate_table, | ||||
| static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, | ||||
| 				struct ieee80211_tx_rate *rate) | ||||
| { | ||||
| 	int rix; | ||||
| @ -1300,7 +1301,7 @@ static void ath_rc_tx_status(struct ath_softc *sc, | ||||
| 			     int final_ts_idx, int xretries, int long_retry) | ||||
| { | ||||
| 	struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||||
| 	struct ath_rate_table *rate_table; | ||||
| 	const struct ath_rate_table *rate_table; | ||||
| 	struct ieee80211_tx_rate *rates = tx_info->status.rates; | ||||
| 	u8 flags; | ||||
| 	u32 i = 0, rix; | ||||
| @ -1354,9 +1355,11 @@ static void ath_rc_tx_status(struct ath_softc *sc, | ||||
| 			 xretries, long_retry); | ||||
| } | ||||
| 
 | ||||
| static struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, | ||||
| 						    enum ieee80211_band band, | ||||
| 						    bool is_ht, bool is_cw_40) | ||||
| static const | ||||
| struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, | ||||
| 					     enum ieee80211_band band, | ||||
| 					     bool is_ht, | ||||
| 					     bool is_cw_40) | ||||
| { | ||||
| 	int mode = 0; | ||||
| 
 | ||||
| @ -1390,7 +1393,7 @@ static void ath_rc_init(struct ath_softc *sc, | ||||
| 			struct ath_rate_priv *ath_rc_priv, | ||||
| 			struct ieee80211_supported_band *sband, | ||||
| 			struct ieee80211_sta *sta, | ||||
| 			struct ath_rate_table *rate_table) | ||||
| 			const struct ath_rate_table *rate_table) | ||||
| { | ||||
| 	struct ath_rateset *rateset = &ath_rc_priv->neg_rates; | ||||
| 	u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates; | ||||
| @ -1568,12 +1571,13 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, | ||||
| 	struct ath_rate_priv *ath_rc_priv = priv_sta; | ||||
| 	__le16 fc = hdr->frame_control; | ||||
| 
 | ||||
| 	/* lowest rate for management and multicast/broadcast frames */ | ||||
| 	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) || | ||||
| 	    !sta) { | ||||
| 	/* lowest rate for management and NO_ACK frames */ | ||||
| 	if (!ieee80211_is_data(fc) || | ||||
| 	    tx_info->flags & IEEE80211_TX_CTL_NO_ACK || !sta) { | ||||
| 		tx_info->control.rates[0].idx = rate_lowest_index(sband, sta); | ||||
| 		tx_info->control.rates[0].count = | ||||
| 			is_multicast_ether_addr(hdr->addr1) ? 1 : ATH_MGT_TXMAXTRY; | ||||
| 			(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) ? | ||||
| 				1 : ATH_MGT_TXMAXTRY; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| @ -1586,7 +1590,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, | ||||
| { | ||||
| 	struct ath_softc *sc = priv; | ||||
| 	struct ath_rate_priv *ath_rc_priv = priv_sta; | ||||
| 	struct ath_rate_table *rate_table = NULL; | ||||
| 	const struct ath_rate_table *rate_table = NULL; | ||||
| 	bool is_cw40, is_sgi40; | ||||
| 	int i, j = 0; | ||||
| 
 | ||||
| @ -1635,7 +1639,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, | ||||
| { | ||||
| 	struct ath_softc *sc = priv; | ||||
| 	struct ath_rate_priv *ath_rc_priv = priv_sta; | ||||
| 	struct ath_rate_table *rate_table = NULL; | ||||
| 	const struct ath_rate_table *rate_table = NULL; | ||||
| 	bool oper_cw40 = false, oper_sgi40; | ||||
| 	bool local_cw40 = (ath_rc_priv->ht_cap & WLAN_RC_40_FLAG) ? | ||||
| 		true : false; | ||||
|  | ||||
| @ -434,7 +434,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | ||||
| static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, | ||||
| 			   struct ath_atx_tid *tid) | ||||
| { | ||||
| 	struct ath_rate_table *rate_table = sc->cur_rate_table; | ||||
| 	const struct ath_rate_table *rate_table = sc->cur_rate_table; | ||||
| 	struct sk_buff *skb; | ||||
| 	struct ieee80211_tx_info *tx_info; | ||||
| 	struct ieee80211_tx_rate *rates; | ||||
| @ -497,7 +497,7 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, | ||||
| static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, | ||||
| 				  struct ath_buf *bf, u16 frmlen) | ||||
| { | ||||
| 	struct ath_rate_table *rt = sc->cur_rate_table; | ||||
| 	const struct ath_rate_table *rt = sc->cur_rate_table; | ||||
| 	struct sk_buff *skb = bf->bf_mpdu; | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||||
| 	u32 nsymbits, nsymbols, mpdudensity; | ||||
| @ -971,7 +971,7 @@ int ath_cabq_update(struct ath_softc *sc) | ||||
| 	else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) | ||||
| 		sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; | ||||
| 
 | ||||
| 	qi.tqi_readyTime = (sc->hw->conf.beacon_int * | ||||
| 	qi.tqi_readyTime = (sc->beacon_interval * | ||||
| 			    sc->config.cabqReadytime) / 100; | ||||
| 	ath_txq_update(sc, qnum, &qi); | ||||
| 
 | ||||
| @ -1407,7 +1407,7 @@ static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb, | ||||
| static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, | ||||
| 			    int width, int half_gi, bool shortPreamble) | ||||
| { | ||||
| 	struct ath_rate_table *rate_table = sc->cur_rate_table; | ||||
| 	const struct ath_rate_table *rate_table = sc->cur_rate_table; | ||||
| 	u32 nbits, nsymbits, duration, nsymbols; | ||||
| 	u8 rc; | ||||
| 	int streams, pktlen; | ||||
| @ -1439,7 +1439,7 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, | ||||
| 
 | ||||
| static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) | ||||
| { | ||||
| 	struct ath_rate_table *rt = sc->cur_rate_table; | ||||
| 	const struct ath_rate_table *rt = sc->cur_rate_table; | ||||
| 	struct ath9k_11n_rate_series series[4]; | ||||
| 	struct sk_buff *skb; | ||||
| 	struct ieee80211_tx_info *tx_info; | ||||
|  | ||||
| @ -279,7 +279,7 @@ static int atmel_config(struct pcmcia_device *link) | ||||
| 	struct pcmcia_device_id *did; | ||||
| 
 | ||||
| 	dev = link->priv; | ||||
| 	did = handle_to_dev(link).driver_data; | ||||
| 	did = dev_get_drvdata(&handle_to_dev(link)); | ||||
| 
 | ||||
| 	DEBUG(0, "atmel_config(0x%p)\n", link); | ||||
| 
 | ||||
|  | ||||
| @ -3468,11 +3468,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 	if (phy->ops->set_rx_antenna) | ||||
| 		phy->ops->set_rx_antenna(dev, antenna); | ||||
| 
 | ||||
| 	/* Update templates for AP/mesh mode. */ | ||||
| 	if (b43_is_mode(wl, NL80211_IFTYPE_AP) || | ||||
| 	    b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) | ||||
| 		b43_set_beacon_int(dev, conf->beacon_int); | ||||
| 
 | ||||
| 	if (!!conf->radio_enabled != phy->radio_on) { | ||||
| 		if (conf->radio_enabled) { | ||||
| 			b43_software_rfkill(dev, RFKILL_STATE_UNBLOCKED); | ||||
| @ -3548,14 +3543,47 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| { | ||||
| 	struct b43_wl *wl = hw_to_b43_wl(hw); | ||||
| 	struct b43_wldev *dev; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 
 | ||||
| 	dev = wl->current_dev; | ||||
| 	if (!dev || b43_status(dev) < B43_STAT_STARTED) | ||||
| 		goto out_unlock_mutex; | ||||
| 
 | ||||
| 	B43_WARN_ON(wl->vif != vif); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		spin_lock_irqsave(&wl->irq_lock, flags); | ||||
| 		if (conf->bssid) | ||||
| 			memcpy(wl->bssid, conf->bssid, ETH_ALEN); | ||||
| 		else | ||||
| 			memset(wl->bssid, 0, ETH_ALEN); | ||||
| 
 | ||||
| 		if (b43_status(dev) >= B43_STAT_INITIALIZED) { | ||||
| 			if (b43_is_mode(wl, NL80211_IFTYPE_AP) || | ||||
| 			    b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) { | ||||
| 				B43_WARN_ON(vif->type != wl->if_type); | ||||
| 				if (changed & BSS_CHANGED_BEACON) | ||||
| 					b43_update_templates(wl); | ||||
| 			} else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) { | ||||
| 				if (changed & BSS_CHANGED_BEACON) | ||||
| 					b43_update_templates(wl); | ||||
| 			} | ||||
| 			b43_write_mac_bssid_templates(dev); | ||||
| 		} | ||||
| 		spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| 	} | ||||
| 
 | ||||
| 	b43_mac_suspend(dev); | ||||
| 
 | ||||
| 	/* Update templates for AP/mesh mode. */ | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT && | ||||
| 	    (b43_is_mode(wl, NL80211_IFTYPE_AP) || | ||||
| 	     b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || | ||||
| 	     b43_is_mode(wl, NL80211_IFTYPE_ADHOC))) | ||||
| 		b43_set_beacon_int(dev, conf->beacon_int); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BASIC_RATES) | ||||
| 		b43_update_basic_rates(dev, conf->basic_rates); | ||||
| 
 | ||||
| @ -3569,8 +3597,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 	b43_mac_enable(dev); | ||||
| out_unlock_mutex: | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, | ||||
| @ -3728,41 +3754,6 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw, | ||||
| 	spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int b43_op_config_interface(struct ieee80211_hw *hw, | ||||
| 				   struct ieee80211_vif *vif, | ||||
| 				   struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct b43_wl *wl = hw_to_b43_wl(hw); | ||||
| 	struct b43_wldev *dev = wl->current_dev; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	if (!dev) | ||||
| 		return -ENODEV; | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 	spin_lock_irqsave(&wl->irq_lock, flags); | ||||
| 	B43_WARN_ON(wl->vif != vif); | ||||
| 	if (conf->bssid) | ||||
| 		memcpy(wl->bssid, conf->bssid, ETH_ALEN); | ||||
| 	else | ||||
| 		memset(wl->bssid, 0, ETH_ALEN); | ||||
| 	if (b43_status(dev) >= B43_STAT_INITIALIZED) { | ||||
| 		if (b43_is_mode(wl, NL80211_IFTYPE_AP) || | ||||
| 		    b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) { | ||||
| 			B43_WARN_ON(vif->type != wl->if_type); | ||||
| 			if (conf->changed & IEEE80211_IFCC_BEACON) | ||||
| 				b43_update_templates(wl); | ||||
| 		} else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) { | ||||
| 			if (conf->changed & IEEE80211_IFCC_BEACON) | ||||
| 				b43_update_templates(wl); | ||||
| 		} | ||||
| 		b43_write_mac_bssid_templates(dev); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Locking: wl->mutex */ | ||||
| static void b43_wireless_core_stop(struct b43_wldev *dev) | ||||
| { | ||||
| @ -4432,7 +4423,6 @@ static const struct ieee80211_ops b43_hw_ops = { | ||||
| 	.remove_interface	= b43_op_remove_interface, | ||||
| 	.config			= b43_op_config, | ||||
| 	.bss_info_changed	= b43_op_bss_info_changed, | ||||
| 	.config_interface	= b43_op_config_interface, | ||||
| 	.configure_filter	= b43_op_configure_filter, | ||||
| 	.set_key		= b43_op_set_key, | ||||
| 	.get_stats		= b43_op_get_stats, | ||||
|  | ||||
| @ -2721,11 +2721,6 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw, | ||||
| 	/* Antennas for RX and management frame TX. */ | ||||
| 	b43legacy_mgmtframe_txantenna(dev, antenna_tx); | ||||
| 
 | ||||
| 	/* Update templates for AP mode. */ | ||||
| 	if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) | ||||
| 		b43legacy_set_beacon_int(dev, conf->beacon_int); | ||||
| 
 | ||||
| 
 | ||||
| 	if (!!conf->radio_enabled != phy->radio_on) { | ||||
| 		if (conf->radio_enabled) { | ||||
| 			b43legacy_radio_turn_on(dev); | ||||
| @ -2809,6 +2804,7 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 	u32 savedirqs; | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 	B43legacy_WARN_ON(wl->vif != vif); | ||||
| 
 | ||||
| 	dev = wl->current_dev; | ||||
| 	phy = &dev->phy; | ||||
| @ -2822,11 +2818,37 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 		goto out_unlock_mutex; | ||||
| 	} | ||||
| 	savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL); | ||||
| 	spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| 	b43legacy_synchronize_irq(dev); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| 		b43legacy_synchronize_irq(dev); | ||||
| 
 | ||||
| 		if (conf->bssid) | ||||
| 			memcpy(wl->bssid, conf->bssid, ETH_ALEN); | ||||
| 		else | ||||
| 			memset(wl->bssid, 0, ETH_ALEN); | ||||
| 
 | ||||
| 		if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) { | ||||
| 			if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) { | ||||
| 				B43legacy_WARN_ON(vif->type != NL80211_IFTYPE_AP); | ||||
| 				if (changed & BSS_CHANGED_BEACON) | ||||
| 					b43legacy_update_templates(wl); | ||||
| 			} else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) { | ||||
| 				if (changed & BSS_CHANGED_BEACON) | ||||
| 					b43legacy_update_templates(wl); | ||||
| 			} | ||||
| 			b43legacy_write_mac_bssid_templates(dev); | ||||
| 		} | ||||
| 		spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| 	} | ||||
| 
 | ||||
| 	b43legacy_mac_suspend(dev); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT && | ||||
| 	    (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || | ||||
| 	     b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) | ||||
| 		b43legacy_set_beacon_int(dev, conf->beacon_int); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BASIC_RATES) | ||||
| 		b43legacy_update_basic_rates(dev, conf->basic_rates); | ||||
| 
 | ||||
| @ -2846,8 +2868,6 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 	spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
|  out_unlock_mutex: | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, | ||||
| @ -2889,40 +2909,6 @@ static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, | ||||
| 	spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int b43legacy_op_config_interface(struct ieee80211_hw *hw, | ||||
| 					 struct ieee80211_vif *vif, | ||||
| 					 struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); | ||||
| 	struct b43legacy_wldev *dev = wl->current_dev; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	if (!dev) | ||||
| 		return -ENODEV; | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 	spin_lock_irqsave(&wl->irq_lock, flags); | ||||
| 	B43legacy_WARN_ON(wl->vif != vif); | ||||
| 	if (conf->bssid) | ||||
| 		memcpy(wl->bssid, conf->bssid, ETH_ALEN); | ||||
| 	else | ||||
| 		memset(wl->bssid, 0, ETH_ALEN); | ||||
| 	if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) { | ||||
| 		if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) { | ||||
| 			B43legacy_WARN_ON(vif->type != NL80211_IFTYPE_AP); | ||||
| 			if (conf->changed & IEEE80211_IFCC_BEACON) | ||||
| 				b43legacy_update_templates(wl); | ||||
| 		} else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) { | ||||
| 			if (conf->changed & IEEE80211_IFCC_BEACON) | ||||
| 				b43legacy_update_templates(wl); | ||||
| 		} | ||||
| 		b43legacy_write_mac_bssid_templates(dev); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&wl->irq_lock, flags); | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Locking: wl->mutex */ | ||||
| static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) | ||||
| { | ||||
| @ -3563,7 +3549,6 @@ static const struct ieee80211_ops b43legacy_hw_ops = { | ||||
| 	.remove_interface	= b43legacy_op_remove_interface, | ||||
| 	.config			= b43legacy_op_dev_config, | ||||
| 	.bss_info_changed	= b43legacy_op_bss_info_changed, | ||||
| 	.config_interface	= b43legacy_op_config_interface, | ||||
| 	.configure_filter	= b43legacy_op_configure_filter, | ||||
| 	.get_stats		= b43legacy_op_get_stats, | ||||
| 	.get_tx_stats		= b43legacy_op_get_tx_stats, | ||||
|  | ||||
| @ -3488,7 +3488,7 @@ static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL); | ||||
| static ssize_t show_cfg(struct device *d, struct device_attribute *attr, | ||||
| 			char *buf) | ||||
| { | ||||
| 	struct ipw2100_priv *p = d->driver_data; | ||||
| 	struct ipw2100_priv *p = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "0x%08x\n", (int)p->config); | ||||
| } | ||||
| 
 | ||||
| @ -3497,7 +3497,7 @@ static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); | ||||
| static ssize_t show_status(struct device *d, struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| { | ||||
| 	struct ipw2100_priv *p = d->driver_data; | ||||
| 	struct ipw2100_priv *p = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "0x%08x\n", (int)p->status); | ||||
| } | ||||
| 
 | ||||
| @ -3506,7 +3506,7 @@ static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); | ||||
| static ssize_t show_capability(struct device *d, struct device_attribute *attr, | ||||
| 			       char *buf) | ||||
| { | ||||
| 	struct ipw2100_priv *p = d->driver_data; | ||||
| 	struct ipw2100_priv *p = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "0x%08x\n", (int)p->capability); | ||||
| } | ||||
| 
 | ||||
| @ -4224,7 +4224,7 @@ static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, | ||||
| 	   1 - SW based RF kill active (sysfs) | ||||
| 	   2 - HW based RF kill active | ||||
| 	   3 - Both HW and SW baed RF kill active */ | ||||
| 	struct ipw2100_priv *priv = (struct ipw2100_priv *)d->driver_data; | ||||
| 	struct ipw2100_priv *priv = dev_get_drvdata(d); | ||||
| 	int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | | ||||
| 	    (rf_kill_active(priv) ? 0x2 : 0x0); | ||||
| 	return sprintf(buf, "%i\n", val); | ||||
|  | ||||
| @ -1527,7 +1527,7 @@ static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led); | ||||
| static ssize_t show_status(struct device *d, | ||||
| 			   struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "0x%08x\n", (int)p->status); | ||||
| } | ||||
| 
 | ||||
| @ -1536,7 +1536,7 @@ static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); | ||||
| static ssize_t show_cfg(struct device *d, struct device_attribute *attr, | ||||
| 			char *buf) | ||||
| { | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "0x%08x\n", (int)p->config); | ||||
| } | ||||
| 
 | ||||
| @ -1545,7 +1545,7 @@ static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); | ||||
| static ssize_t show_nic_type(struct device *d, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "TYPE: %d\n", priv->nic_type); | ||||
| } | ||||
| 
 | ||||
| @ -1555,7 +1555,7 @@ static ssize_t show_ucode_version(struct device *d, | ||||
| 				  struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	u32 len = sizeof(u32), tmp = 0; | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len)) | ||||
| 		return 0; | ||||
| @ -1569,7 +1569,7 @@ static ssize_t show_rtc(struct device *d, struct device_attribute *attr, | ||||
| 			char *buf) | ||||
| { | ||||
| 	u32 len = sizeof(u32), tmp = 0; | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len)) | ||||
| 		return 0; | ||||
| @ -1586,14 +1586,15 @@ static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL); | ||||
| static ssize_t show_eeprom_delay(struct device *d, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 	int n = p->eeprom_delay; | ||||
| 	return sprintf(buf, "%i\n", n); | ||||
| } | ||||
| static ssize_t store_eeprom_delay(struct device *d, | ||||
| 				  struct device_attribute *attr, | ||||
| 				  const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 	sscanf(buf, "%i", &p->eeprom_delay); | ||||
| 	return strnlen(buf, count); | ||||
| } | ||||
| @ -1605,7 +1606,7 @@ static ssize_t show_command_event_reg(struct device *d, | ||||
| 				      struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	u32 reg = 0; | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT); | ||||
| 	return sprintf(buf, "0x%08x\n", reg); | ||||
| @ -1615,7 +1616,7 @@ static ssize_t store_command_event_reg(struct device *d, | ||||
| 				       const char *buf, size_t count) | ||||
| { | ||||
| 	u32 reg; | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	sscanf(buf, "%x", ®); | ||||
| 	ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg); | ||||
| @ -1629,7 +1630,7 @@ static ssize_t show_mem_gpio_reg(struct device *d, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	u32 reg = 0; | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	reg = ipw_read_reg32(p, 0x301100); | ||||
| 	return sprintf(buf, "0x%08x\n", reg); | ||||
| @ -1639,7 +1640,7 @@ static ssize_t store_mem_gpio_reg(struct device *d, | ||||
| 				  const char *buf, size_t count) | ||||
| { | ||||
| 	u32 reg; | ||||
| 	struct ipw_priv *p = d->driver_data; | ||||
| 	struct ipw_priv *p = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	sscanf(buf, "%x", ®); | ||||
| 	ipw_write_reg32(p, 0x301100, reg); | ||||
| @ -1653,7 +1654,7 @@ static ssize_t show_indirect_dword(struct device *d, | ||||
| 				   struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	u32 reg = 0; | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (priv->status & STATUS_INDIRECT_DWORD) | ||||
| 		reg = ipw_read_reg32(priv, priv->indirect_dword); | ||||
| @ -1666,7 +1667,7 @@ static ssize_t store_indirect_dword(struct device *d, | ||||
| 				    struct device_attribute *attr, | ||||
| 				    const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	sscanf(buf, "%x", &priv->indirect_dword); | ||||
| 	priv->status |= STATUS_INDIRECT_DWORD; | ||||
| @ -1680,7 +1681,7 @@ static ssize_t show_indirect_byte(struct device *d, | ||||
| 				  struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	u8 reg = 0; | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (priv->status & STATUS_INDIRECT_BYTE) | ||||
| 		reg = ipw_read_reg8(priv, priv->indirect_byte); | ||||
| @ -1693,7 +1694,7 @@ static ssize_t store_indirect_byte(struct device *d, | ||||
| 				   struct device_attribute *attr, | ||||
| 				   const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	sscanf(buf, "%x", &priv->indirect_byte); | ||||
| 	priv->status |= STATUS_INDIRECT_BYTE; | ||||
| @ -1707,7 +1708,7 @@ static ssize_t show_direct_dword(struct device *d, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	u32 reg = 0; | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (priv->status & STATUS_DIRECT_DWORD) | ||||
| 		reg = ipw_read32(priv, priv->direct_dword); | ||||
| @ -1720,7 +1721,7 @@ static ssize_t store_direct_dword(struct device *d, | ||||
| 				  struct device_attribute *attr, | ||||
| 				  const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	sscanf(buf, "%x", &priv->direct_dword); | ||||
| 	priv->status |= STATUS_DIRECT_DWORD; | ||||
| @ -1747,7 +1748,7 @@ static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, | ||||
| 	   1 - SW based RF kill active (sysfs) | ||||
| 	   2 - HW based RF kill active | ||||
| 	   3 - Both HW and SW baed RF kill active */ | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 	int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | | ||||
| 	    (rf_kill_active(priv) ? 0x2 : 0x0); | ||||
| 	return sprintf(buf, "%i\n", val); | ||||
| @ -1791,7 +1792,7 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio) | ||||
| static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, | ||||
| 			     const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *priv = d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	ipw_radio_kill_sw(priv, buf[0] == '1'); | ||||
| 
 | ||||
| @ -1803,7 +1804,7 @@ static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); | ||||
| static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr, | ||||
| 			       char *buf) | ||||
| { | ||||
| 	struct ipw_priv *priv = (struct ipw_priv *)d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 	int pos = 0, len = 0; | ||||
| 	if (priv->config & CFG_SPEED_SCAN) { | ||||
| 		while (priv->speed_scan[pos] != 0) | ||||
| @ -1818,7 +1819,7 @@ static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr, | ||||
| static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr, | ||||
| 				const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *priv = (struct ipw_priv *)d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 	int channel, pos = 0; | ||||
| 	const char *p = buf; | ||||
| 
 | ||||
| @ -1857,14 +1858,14 @@ static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan, | ||||
| static ssize_t show_net_stats(struct device *d, struct device_attribute *attr, | ||||
| 			      char *buf) | ||||
| { | ||||
| 	struct ipw_priv *priv = (struct ipw_priv *)d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0'); | ||||
| } | ||||
| 
 | ||||
| static ssize_t store_net_stats(struct device *d, struct device_attribute *attr, | ||||
| 			       const char *buf, size_t count) | ||||
| { | ||||
| 	struct ipw_priv *priv = (struct ipw_priv *)d->driver_data; | ||||
| 	struct ipw_priv *priv = dev_get_drvdata(d); | ||||
| 	if (buf[0] == '1') | ||||
| 		priv->config |= CFG_NET_STATS; | ||||
| 	else | ||||
|  | ||||
| @ -683,11 +683,10 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, | ||||
| 	if (sta) | ||||
| 		rate_mask = sta->supp_rates[sband->band]; | ||||
| 
 | ||||
| 	/* Send management frames and broadcast/multicast data using lowest
 | ||||
| 	 * rate. */ | ||||
| 	/* Send management frames and NO_ACK data using lowest rate. */ | ||||
| 	fc = le16_to_cpu(hdr->frame_control); | ||||
| 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || | ||||
| 	    is_multicast_ether_addr(hdr->addr1) || | ||||
| 	    info->flags & IEEE80211_TX_CTL_NO_ACK || | ||||
| 	    !sta || !priv_sta) { | ||||
| 		IWL_DEBUG_RATE(priv, "leave: No STA priv data to update!\n"); | ||||
| 		if (!rate_mask) | ||||
| @ -696,6 +695,8 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, | ||||
| 		else | ||||
| 			info->control.rates[0].idx = | ||||
| 					rate_lowest_index(sband, sta); | ||||
| 		if (info->flags & IEEE80211_TX_CTL_NO_ACK) | ||||
| 			info->control.rates[0].count = 1; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -172,7 +172,7 @@ struct iwl_lq_sta { | ||||
| }; | ||||
| 
 | ||||
| static void rs_rate_scale_perform(struct iwl_priv *priv, | ||||
| 				   struct ieee80211_hdr *hdr, | ||||
| 				   struct sk_buff *skb, | ||||
| 				   struct ieee80211_sta *sta, | ||||
| 				   struct iwl_lq_sta *lq_sta); | ||||
| static void rs_fill_link_cmd(const struct iwl_priv *priv, | ||||
| @ -829,7 +829,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, | ||||
| 	IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); | ||||
| 
 | ||||
| 	if (!ieee80211_is_data(hdr->frame_control) || | ||||
| 	    is_multicast_ether_addr(hdr->addr1)) | ||||
| 	    info->flags & IEEE80211_TX_CTL_NO_ACK) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* This packet was aggregated but doesn't carry rate scale info */ | ||||
| @ -995,7 +995,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, | ||||
| 
 | ||||
| 	/* See if there's a better rate or modulation mode to try. */ | ||||
| 	if (sta && sta->supp_rates[sband->band]) | ||||
| 		rs_rate_scale_perform(priv, hdr, sta, lq_sta); | ||||
| 		rs_rate_scale_perform(priv, skb, sta, lq_sta); | ||||
| out: | ||||
| 	return; | ||||
| } | ||||
| @ -1207,8 +1207,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, | ||||
| 	tbl->action = 0; | ||||
| 	rate_mask = lq_sta->active_mimo2_rate; | ||||
| 
 | ||||
| 	if (priv->current_ht_config.supported_chan_width | ||||
| 					== IWL_CHANNEL_WIDTH_40MHZ) | ||||
| 	if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) | ||||
| 		tbl->is_fat = 1; | ||||
| 	else | ||||
| 		tbl->is_fat = 0; | ||||
| @ -1273,8 +1272,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, | ||||
| 	tbl->action = 0; | ||||
| 	rate_mask = lq_sta->active_mimo3_rate; | ||||
| 
 | ||||
| 	if (priv->current_ht_config.supported_chan_width | ||||
| 					== IWL_CHANNEL_WIDTH_40MHZ) | ||||
| 	if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) | ||||
| 		tbl->is_fat = 1; | ||||
| 	else | ||||
| 		tbl->is_fat = 0; | ||||
| @ -1332,8 +1330,7 @@ static int rs_switch_to_siso(struct iwl_priv *priv, | ||||
| 	tbl->action = 0; | ||||
| 	rate_mask = lq_sta->active_siso_rate; | ||||
| 
 | ||||
| 	if (priv->current_ht_config.supported_chan_width | ||||
| 	    == IWL_CHANNEL_WIDTH_40MHZ) | ||||
| 	if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) | ||||
| 		tbl->is_fat = 1; | ||||
| 	else | ||||
| 		tbl->is_fat = 0; | ||||
| @ -1975,12 +1972,14 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta) | ||||
|  * Do rate scaling and search for new modulation mode. | ||||
|  */ | ||||
| static void rs_rate_scale_perform(struct iwl_priv *priv, | ||||
| 				  struct ieee80211_hdr *hdr, | ||||
| 				  struct sk_buff *skb, | ||||
| 				  struct ieee80211_sta *sta, | ||||
| 				  struct iwl_lq_sta *lq_sta) | ||||
| { | ||||
| 	struct ieee80211_hw *hw = priv->hw; | ||||
| 	struct ieee80211_conf *conf = &hw->conf; | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 	int low = IWL_RATE_INVALID; | ||||
| 	int high = IWL_RATE_INVALID; | ||||
| 	int index; | ||||
| @ -2006,11 +2005,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, | ||||
| 
 | ||||
| 	IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); | ||||
| 
 | ||||
| 	/* Send management frames and broadcast/multicast data using
 | ||||
| 	 * lowest rate. */ | ||||
| 	/* Send management frames and NO_ACK data using lowest rate. */ | ||||
| 	/* TODO: this could probably be improved.. */ | ||||
| 	if (!ieee80211_is_data(hdr->frame_control) || | ||||
| 	    is_multicast_ether_addr(hdr->addr1)) | ||||
| 	    info->flags & IEEE80211_TX_CTL_NO_ACK) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!sta || !lq_sta) | ||||
| @ -2450,16 +2448,17 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, | ||||
| 	if (sta) | ||||
| 		mask_bit = sta->supp_rates[sband->band]; | ||||
| 
 | ||||
| 	/* Send management frames and broadcast/multicast data using lowest
 | ||||
| 	 * rate. */ | ||||
| 	/* Send management frames and NO_ACK data using lowest rate. */ | ||||
| 	if (!ieee80211_is_data(hdr->frame_control) || | ||||
| 	    is_multicast_ether_addr(hdr->addr1) || !sta || !lq_sta) { | ||||
| 	    info->flags & IEEE80211_TX_CTL_NO_ACK || !sta || !lq_sta) { | ||||
| 		if (!mask_bit) | ||||
| 			info->control.rates[0].idx = | ||||
| 					rate_lowest_index(sband, NULL); | ||||
| 		else | ||||
| 			info->control.rates[0].idx = | ||||
| 					rate_lowest_index(sband, sta); | ||||
| 		if (info->flags & IEEE80211_TX_CTL_NO_ACK) | ||||
| 			info->control.rates[0].count = 1; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -567,7 +567,8 @@ static void iwl_setup_rxon_timing(struct iwl_priv *priv) | ||||
| 		beacon_int = iwl_adjust_beacon_interval(priv->beacon_int); | ||||
| 		priv->rxon_timing.atim_window = 0; | ||||
| 	} else { | ||||
| 		beacon_int = iwl_adjust_beacon_interval(conf->beacon_int); | ||||
| 		beacon_int = iwl_adjust_beacon_interval( | ||||
| 			priv->vif->bss_conf.beacon_int); | ||||
| 
 | ||||
| 		/* TODO: we need to get atim_window from upper stack
 | ||||
| 		 * for now we set to 0 */ | ||||
| @ -2267,7 +2268,7 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw, | ||||
| static ssize_t show_debug_level(struct device *d, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%08X\n", priv->debug_level); | ||||
| } | ||||
| @ -2275,7 +2276,7 @@ static ssize_t store_debug_level(struct device *d, | ||||
| 				struct device_attribute *attr, | ||||
| 				 const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	unsigned long val; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -2298,7 +2299,7 @@ static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, | ||||
| static ssize_t show_version(struct device *d, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	struct iwl_alive_resp *palive = &priv->card_alive; | ||||
| 	ssize_t pos = 0; | ||||
| 	u16 eeprom_ver; | ||||
| @ -2329,7 +2330,7 @@ static DEVICE_ATTR(version, S_IWUSR | S_IRUGO, show_version, NULL); | ||||
| static ssize_t show_temperature(struct device *d, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (!iwl_is_alive(priv)) | ||||
| 		return -EAGAIN; | ||||
| @ -2342,7 +2343,7 @@ static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); | ||||
| static ssize_t show_tx_power(struct device *d, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (!iwl_is_ready_rf(priv)) | ||||
| 		return sprintf(buf, "off\n"); | ||||
| @ -2354,7 +2355,7 @@ static ssize_t store_tx_power(struct device *d, | ||||
| 			      struct device_attribute *attr, | ||||
| 			      const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	unsigned long val; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -2372,7 +2373,7 @@ static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); | ||||
| static ssize_t show_flags(struct device *d, | ||||
| 			  struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%04X\n", priv->active_rxon.flags); | ||||
| } | ||||
| @ -2381,7 +2382,7 @@ static ssize_t store_flags(struct device *d, | ||||
| 			   struct device_attribute *attr, | ||||
| 			   const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	unsigned long val; | ||||
| 	u32 flags; | ||||
| 	int ret = strict_strtoul(buf, 0, &val); | ||||
| @ -2410,7 +2411,7 @@ static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, show_flags, store_flags); | ||||
| static ssize_t show_filter_flags(struct device *d, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%04X\n", | ||||
| 		le32_to_cpu(priv->active_rxon.filter_flags)); | ||||
| @ -2420,7 +2421,7 @@ static ssize_t store_filter_flags(struct device *d, | ||||
| 				  struct device_attribute *attr, | ||||
| 				  const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	unsigned long val; | ||||
| 	u32 filter_flags; | ||||
| 	int ret = strict_strtoul(buf, 0, &val); | ||||
| @ -2622,7 +2623,6 @@ static struct ieee80211_ops iwl_hw_ops = { | ||||
| 	.add_interface = iwl_mac_add_interface, | ||||
| 	.remove_interface = iwl_mac_remove_interface, | ||||
| 	.config = iwl_mac_config, | ||||
| 	.config_interface = iwl_mac_config_interface, | ||||
| 	.configure_filter = iwl_configure_filter, | ||||
| 	.set_key = iwl_mac_set_key, | ||||
| 	.update_tkip_key = iwl_mac_update_tkip_key, | ||||
|  | ||||
| @ -1298,8 +1298,7 @@ int iwl_setup_mac(struct iwl_priv *priv) | ||||
| 	hw->flags = IEEE80211_HW_SIGNAL_DBM | | ||||
| 		    IEEE80211_HW_NOISE_DBM | | ||||
| 		    IEEE80211_HW_AMPDU_AGGREGATION | | ||||
| 		    IEEE80211_HW_SPECTRUM_MGMT | | ||||
| 		    IEEE80211_HW_SUPPORTS_PS; | ||||
| 		    IEEE80211_HW_SPECTRUM_MGMT; | ||||
| 	hw->wiphy->interface_modes = | ||||
| 		BIT(NL80211_IFTYPE_STATION) | | ||||
| 		BIT(NL80211_IFTYPE_ADHOC); | ||||
| @ -1313,7 +1312,6 @@ int iwl_setup_mac(struct iwl_priv *priv) | ||||
| 	/* Default value; 4 EDCA QOS priorities */ | ||||
| 	hw->queues = 4; | ||||
| 
 | ||||
| 	hw->conf.beacon_int = 100; | ||||
| 	hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; | ||||
| 
 | ||||
| 	if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) | ||||
| @ -2239,15 +2237,69 @@ static void iwl_ht_conf(struct iwl_priv *priv, | ||||
| 
 | ||||
| #define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) | ||||
| void iwl_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 				     struct ieee80211_vif *vif, | ||||
| 				     struct ieee80211_bss_conf *bss_conf, | ||||
| 				     u32 changes) | ||||
| 			  struct ieee80211_vif *vif, | ||||
| 			  struct ieee80211_bss_conf *bss_conf, | ||||
| 			  u32 changes) | ||||
| { | ||||
| 	struct iwl_priv *priv = hw->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes); | ||||
| 
 | ||||
| 	if (!iwl_is_alive(priv)) | ||||
| 		return; | ||||
| 
 | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_BEACON && | ||||
| 	    priv->iw_mode == NL80211_IFTYPE_AP) { | ||||
| 		dev_kfree_skb(priv->ibss_beacon); | ||||
| 		priv->ibss_beacon = ieee80211_beacon_get(hw, vif); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((changes & BSS_CHANGED_BSSID) && !iwl_is_rfkill(priv)) { | ||||
| 		/* If there is currently a HW scan going on in the background
 | ||||
| 		 * then we need to cancel it else the RXON below will fail. */ | ||||
| 		if (iwl_scan_cancel_timeout(priv, 100)) { | ||||
| 			IWL_WARN(priv, "Aborted scan still in progress " | ||||
| 				    "after 100ms\n"); | ||||
| 			IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); | ||||
| 			mutex_unlock(&priv->mutex); | ||||
| 			return; | ||||
| 		} | ||||
| 		memcpy(priv->staging_rxon.bssid_addr, | ||||
| 		       bss_conf->bssid, ETH_ALEN); | ||||
| 
 | ||||
| 		/* TODO: Audit driver for usage of these members and see
 | ||||
| 		 * if mac80211 deprecates them (priv->bssid looks like it | ||||
| 		 * shouldn't be there, but I haven't scanned the IBSS code | ||||
| 		 * to verify) - jpk */ | ||||
| 		memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 
 | ||||
| 		if (priv->iw_mode == NL80211_IFTYPE_AP) | ||||
| 			iwlcore_config_ap(priv); | ||||
| 		else { | ||||
| 			int rc = iwlcore_commit_rxon(priv); | ||||
| 			if ((priv->iw_mode == NL80211_IFTYPE_STATION) && rc) | ||||
| 				iwl_rxon_add_station( | ||||
| 					priv, priv->active_rxon.bssid_addr, 1); | ||||
| 		} | ||||
| 	} else if (!iwl_is_rfkill(priv)) { | ||||
| 		iwl_scan_cancel_timeout(priv, 100); | ||||
| 		priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; | ||||
| 		iwlcore_commit_rxon(priv); | ||||
| 	} | ||||
| 
 | ||||
| 	if (priv->iw_mode == NL80211_IFTYPE_ADHOC && | ||||
| 	    changes & BSS_CHANGED_BEACON) { | ||||
| 		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||||
| 
 | ||||
| 		if (beacon) | ||||
| 			iwl_mac_beacon_update(hw, beacon); | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_ERP_PREAMBLE) { | ||||
| 		IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n", | ||||
| 				   bss_conf->use_short_preamble); | ||||
| @ -2306,7 +2358,7 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 					&priv->staging_rxon, | ||||
| 					sizeof(struct iwl_rxon_cmd)); | ||||
| 	} | ||||
| 
 | ||||
| 	IWL_DEBUG_MAC80211(priv, "leave\n"); | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_bss_info_changed); | ||||
| 
 | ||||
| @ -2590,106 +2642,6 @@ out: | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_mac_config); | ||||
| 
 | ||||
| int iwl_mac_config_interface(struct ieee80211_hw *hw, | ||||
| 					struct ieee80211_vif *vif, | ||||
| 				    struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct iwl_priv *priv = hw->priv; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	if (conf == NULL) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	if (priv->vif != vif) { | ||||
| 		IWL_DEBUG_MAC80211(priv, "leave - priv->vif != vif\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (priv->iw_mode == NL80211_IFTYPE_ADHOC && | ||||
| 	    conf->changed & IEEE80211_IFCC_BEACON) { | ||||
| 		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||||
| 		if (!beacon) | ||||
| 			return -ENOMEM; | ||||
| 		mutex_lock(&priv->mutex); | ||||
| 		rc = iwl_mac_beacon_update(hw, beacon); | ||||
| 		mutex_unlock(&priv->mutex); | ||||
| 		if (rc) | ||||
| 			return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!iwl_is_alive(priv)) | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	mutex_lock(&priv->mutex); | ||||
| 
 | ||||
| 	if (conf->bssid) | ||||
| 		IWL_DEBUG_MAC80211(priv, "bssid: %pM\n", conf->bssid); | ||||
| 
 | ||||
| /*
 | ||||
|  * very dubious code was here; the probe filtering flag is never set: | ||||
|  * | ||||
| 	if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) && | ||||
| 	    !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) { | ||||
|  */ | ||||
| 
 | ||||
| 	if (priv->iw_mode == NL80211_IFTYPE_AP) { | ||||
| 		if (!conf->bssid) { | ||||
| 			conf->bssid = priv->mac_addr; | ||||
| 			memcpy(priv->bssid, priv->mac_addr, ETH_ALEN); | ||||
| 			IWL_DEBUG_MAC80211(priv, "bssid was set to: %pM\n", | ||||
| 					   conf->bssid); | ||||
| 		} | ||||
| 		if (priv->ibss_beacon) | ||||
| 			dev_kfree_skb(priv->ibss_beacon); | ||||
| 
 | ||||
| 		priv->ibss_beacon = ieee80211_beacon_get(hw, vif); | ||||
| 	} | ||||
| 
 | ||||
| 	if (iwl_is_rfkill(priv)) | ||||
| 		goto done; | ||||
| 
 | ||||
| 	if (conf->bssid && !is_zero_ether_addr(conf->bssid) && | ||||
| 	    !is_multicast_ether_addr(conf->bssid)) { | ||||
| 		/* If there is currently a HW scan going on in the background
 | ||||
| 		 * then we need to cancel it else the RXON below will fail. */ | ||||
| 		if (iwl_scan_cancel_timeout(priv, 100)) { | ||||
| 			IWL_WARN(priv, "Aborted scan still in progress " | ||||
| 				    "after 100ms\n"); | ||||
| 			IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); | ||||
| 			mutex_unlock(&priv->mutex); | ||||
| 			return -EAGAIN; | ||||
| 		} | ||||
| 		memcpy(priv->staging_rxon.bssid_addr, conf->bssid, ETH_ALEN); | ||||
| 
 | ||||
| 		/* TODO: Audit driver for usage of these members and see
 | ||||
| 		 * if mac80211 deprecates them (priv->bssid looks like it | ||||
| 		 * shouldn't be there, but I haven't scanned the IBSS code | ||||
| 		 * to verify) - jpk */ | ||||
| 		memcpy(priv->bssid, conf->bssid, ETH_ALEN); | ||||
| 
 | ||||
| 		if (priv->iw_mode == NL80211_IFTYPE_AP) | ||||
| 			iwlcore_config_ap(priv); | ||||
| 		else { | ||||
| 			rc = iwlcore_commit_rxon(priv); | ||||
| 			if ((priv->iw_mode == NL80211_IFTYPE_STATION) && rc) | ||||
| 				iwl_rxon_add_station( | ||||
| 					priv, priv->active_rxon.bssid_addr, 1); | ||||
| 		} | ||||
| 
 | ||||
| 	} else { | ||||
| 		iwl_scan_cancel_timeout(priv, 100); | ||||
| 		priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; | ||||
| 		iwlcore_commit_rxon(priv); | ||||
| 	} | ||||
| 
 | ||||
|  done: | ||||
| 	IWL_DEBUG_MAC80211(priv, "leave\n"); | ||||
| 	mutex_unlock(&priv->mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL(iwl_mac_config_interface); | ||||
| 
 | ||||
| int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, | ||||
| 			 struct ieee80211_tx_queue_stats *stats) | ||||
| { | ||||
| @ -2751,7 +2703,7 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) | ||||
| 
 | ||||
| 	priv->ibss_beacon = NULL; | ||||
| 
 | ||||
| 	priv->beacon_int = priv->hw->conf.beacon_int; | ||||
| 	priv->beacon_int = priv->vif->bss_conf.beacon_int; | ||||
| 	priv->timestamp = 0; | ||||
| 	if ((priv->iw_mode == NL80211_IFTYPE_STATION)) | ||||
| 		priv->beacon_int = 0; | ||||
|  | ||||
| @ -281,9 +281,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, | ||||
| 				 struct ieee80211_if_init_conf *conf); | ||||
| int iwl_mac_config(struct ieee80211_hw *hw, u32 changed); | ||||
| void iwl_config_ap(struct iwl_priv *priv); | ||||
| int iwl_mac_config_interface(struct ieee80211_hw *hw, | ||||
| 				struct ieee80211_vif *vif, | ||||
| 				struct ieee80211_if_conf *conf); | ||||
| int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, | ||||
| 			 struct ieee80211_tx_queue_stats *stats); | ||||
| void iwl_mac_reset_tsf(struct ieee80211_hw *hw); | ||||
|  | ||||
| @ -490,7 +490,7 @@ void iwl_clear_stations_table(struct iwl_priv *priv) | ||||
| 	/* keep track of static keys */ | ||||
| 	for (i = 0; i < WEP_KEYS_MAX ; i++) { | ||||
| 		if (priv->wep_keys[i].key_size) | ||||
| 			test_and_set_bit(i, &priv->ucode_key_table); | ||||
| 			set_bit(i, &priv->ucode_key_table); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&priv->sta_lock, flags); | ||||
|  | ||||
| @ -551,7 +551,8 @@ static void iwl3945_setup_rxon_timing(struct iwl_priv *priv) | ||||
| 		priv->rxon_timing.atim_window = 0; | ||||
| 	} else { | ||||
| 		priv->rxon_timing.beacon_interval = | ||||
| 			iwl3945_adjust_beacon_interval(conf->beacon_int); | ||||
| 			iwl3945_adjust_beacon_interval( | ||||
| 				priv->vif->bss_conf.beacon_int); | ||||
| 		/* TODO: we need to get atim_window from upper stack
 | ||||
| 		 * for now we set to 0 */ | ||||
| 		priv->rxon_timing.atim_window = 0; | ||||
| @ -1343,15 +1344,24 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv) | ||||
| 	struct list_head *element; | ||||
| 	struct iwl_rx_mem_buffer *rxb; | ||||
| 	unsigned long flags; | ||||
| 	spin_lock_irqsave(&rxq->lock, flags); | ||||
| 	while (!list_empty(&rxq->rx_used)) { | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		spin_lock_irqsave(&rxq->lock, flags); | ||||
| 
 | ||||
| 		if (list_empty(&rxq->rx_used)) { | ||||
| 			spin_unlock_irqrestore(&rxq->lock, flags); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		element = rxq->rx_used.next; | ||||
| 		rxb = list_entry(element, struct iwl_rx_mem_buffer, list); | ||||
| 		list_del(element); | ||||
| 		spin_unlock_irqrestore(&rxq->lock, flags); | ||||
| 
 | ||||
| 		/* Alloc a new receive buffer */ | ||||
| 		rxb->skb = | ||||
| 		    alloc_skb(priv->hw_params.rx_buf_size, | ||||
| 				__GFP_NOWARN | GFP_ATOMIC); | ||||
| 				GFP_KERNEL); | ||||
| 		if (!rxb->skb) { | ||||
| 			if (net_ratelimit()) | ||||
| 				IWL_CRIT(priv, ": Can not allocate SKB buffers\n"); | ||||
| @ -1369,18 +1379,18 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv) | ||||
| 		 */ | ||||
| 		skb_reserve(rxb->skb, 4); | ||||
| 
 | ||||
| 		priv->alloc_rxb_skb++; | ||||
| 		list_del(element); | ||||
| 
 | ||||
| 		/* Get physical address of RB/SKB */ | ||||
| 		rxb->real_dma_addr = pci_map_single(priv->pci_dev, | ||||
| 						rxb->skb->data, | ||||
| 						priv->hw_params.rx_buf_size, | ||||
| 						PCI_DMA_FROMDEVICE); | ||||
| 
 | ||||
| 		spin_lock_irqsave(&rxq->lock, flags); | ||||
| 		list_add_tail(&rxb->list, &rxq->rx_free); | ||||
| 		priv->alloc_rxb_skb++; | ||||
| 		rxq->free_count++; | ||||
| 		spin_unlock_irqrestore(&rxq->lock, flags); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&rxq->lock, flags); | ||||
| } | ||||
| 
 | ||||
| void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) | ||||
| @ -1413,18 +1423,6 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) | ||||
| 	spin_unlock_irqrestore(&rxq->lock, flags); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * this should be called while priv->lock is locked | ||||
|  */ | ||||
| static void __iwl3945_rx_replenish(void *data) | ||||
| { | ||||
| 	struct iwl_priv *priv = data; | ||||
| 
 | ||||
| 	iwl3945_rx_allocate(priv); | ||||
| 	iwl3945_rx_queue_restock(priv); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void iwl3945_rx_replenish(void *data) | ||||
| { | ||||
| 	struct iwl_priv *priv = data; | ||||
| @ -1642,7 +1640,7 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) | ||||
| 			count++; | ||||
| 			if (count >= 8) { | ||||
| 				priv->rxq.read = i; | ||||
| 				__iwl3945_rx_replenish(priv); | ||||
| 				iwl3945_rx_queue_restock(priv); | ||||
| 				count = 0; | ||||
| 			} | ||||
| 		} | ||||
| @ -3596,7 +3594,7 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, | ||||
| static ssize_t show_debug_level(struct device *d, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%08X\n", priv->debug_level); | ||||
| } | ||||
| @ -3604,7 +3602,7 @@ static ssize_t store_debug_level(struct device *d, | ||||
| 				struct device_attribute *attr, | ||||
| 				 const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	unsigned long val; | ||||
| 	int ret; | ||||
| 
 | ||||
| @ -3625,7 +3623,7 @@ static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, | ||||
| static ssize_t show_temperature(struct device *d, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	if (!iwl_is_alive(priv)) | ||||
| 		return -EAGAIN; | ||||
| @ -3638,7 +3636,7 @@ static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); | ||||
| static ssize_t show_tx_power(struct device *d, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	return sprintf(buf, "%d\n", priv->tx_power_user_lmt); | ||||
| } | ||||
| 
 | ||||
| @ -3646,7 +3644,7 @@ static ssize_t store_tx_power(struct device *d, | ||||
| 			      struct device_attribute *attr, | ||||
| 			      const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	char *p = (char *)buf; | ||||
| 	u32 val; | ||||
| 
 | ||||
| @ -3664,7 +3662,7 @@ static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); | ||||
| static ssize_t show_flags(struct device *d, | ||||
| 			  struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%04X\n", priv->active_rxon.flags); | ||||
| } | ||||
| @ -3673,7 +3671,7 @@ static ssize_t store_flags(struct device *d, | ||||
| 			   struct device_attribute *attr, | ||||
| 			   const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	u32 flags = simple_strtoul(buf, NULL, 0); | ||||
| 
 | ||||
| 	mutex_lock(&priv->mutex); | ||||
| @ -3698,7 +3696,7 @@ static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, show_flags, store_flags); | ||||
| static ssize_t show_filter_flags(struct device *d, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%04X\n", | ||||
| 		le32_to_cpu(priv->active_rxon.filter_flags)); | ||||
| @ -3708,7 +3706,7 @@ static ssize_t store_filter_flags(struct device *d, | ||||
| 				  struct device_attribute *attr, | ||||
| 				  const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	u32 filter_flags = simple_strtoul(buf, NULL, 0); | ||||
| 
 | ||||
| 	mutex_lock(&priv->mutex); | ||||
| @ -3993,7 +3991,7 @@ static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, show_antenna, store_antenna); | ||||
| static ssize_t show_status(struct device *d, | ||||
| 			   struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	if (!iwl_is_alive(priv)) | ||||
| 		return -EAGAIN; | ||||
| 	return sprintf(buf, "0x%08x\n", (int)priv->status); | ||||
| @ -4005,10 +4003,11 @@ static ssize_t dump_error_log(struct device *d, | ||||
| 			      struct device_attribute *attr, | ||||
| 			      const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	char *p = (char *)buf; | ||||
| 
 | ||||
| 	if (p[0] == '1') | ||||
| 		iwl3945_dump_nic_error_log((struct iwl_priv *)d->driver_data); | ||||
| 		iwl3945_dump_nic_error_log(priv); | ||||
| 
 | ||||
| 	return strnlen(buf, count); | ||||
| } | ||||
| @ -4019,10 +4018,11 @@ static ssize_t dump_event_log(struct device *d, | ||||
| 			      struct device_attribute *attr, | ||||
| 			      const char *buf, size_t count) | ||||
| { | ||||
| 	struct iwl_priv *priv = dev_get_drvdata(d); | ||||
| 	char *p = (char *)buf; | ||||
| 
 | ||||
| 	if (p[0] == '1') | ||||
| 		iwl3945_dump_nic_event_log((struct iwl_priv *)d->driver_data); | ||||
| 		iwl3945_dump_nic_event_log(priv); | ||||
| 
 | ||||
| 	return strnlen(buf, count); | ||||
| } | ||||
| @ -4104,7 +4104,6 @@ static struct ieee80211_ops iwl3945_hw_ops = { | ||||
| 	.add_interface = iwl_mac_add_interface, | ||||
| 	.remove_interface = iwl_mac_remove_interface, | ||||
| 	.config = iwl_mac_config, | ||||
| 	.config_interface = iwl_mac_config_interface, | ||||
| 	.configure_filter = iwl_configure_filter, | ||||
| 	.set_key = iwl3945_mac_set_key, | ||||
| 	.get_tx_stats = iwl_mac_get_tx_stats, | ||||
| @ -4210,8 +4209,6 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) | ||||
| 	/* Default value; 4 EDCA QOS priorities */ | ||||
| 	hw->queues = 4; | ||||
| 
 | ||||
| 	hw->conf.beacon_int = 100; | ||||
| 
 | ||||
| 	if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) | ||||
| 		priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = | ||||
| 			&priv->bands[IEEE80211_BAND_2GHZ]; | ||||
|  | ||||
| @ -366,36 +366,6 @@ static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lbtf_op_config_interface(struct ieee80211_hw *hw, | ||||
| 			struct ieee80211_vif *vif, | ||||
| 			struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct lbtf_private *priv = hw->priv; | ||||
| 	struct sk_buff *beacon; | ||||
| 
 | ||||
| 	switch (priv->vif->type) { | ||||
| 	case NL80211_IFTYPE_AP: | ||||
| 	case NL80211_IFTYPE_MESH_POINT: | ||||
| 		beacon = ieee80211_beacon_get(hw, vif); | ||||
| 		if (beacon) { | ||||
| 			lbtf_beacon_set(priv, beacon); | ||||
| 			kfree_skb(beacon); | ||||
| 			lbtf_beacon_ctrl(priv, 1, hw->conf.beacon_int); | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (conf->bssid) { | ||||
| 		u8 null_bssid[ETH_ALEN] = {0}; | ||||
| 		bool activate = compare_ether_addr(conf->bssid, null_bssid); | ||||
| 		lbtf_set_bssid(priv, activate, conf->bssid); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #define SUPPORTED_FIF_FLAGS  (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) | ||||
| static void lbtf_op_configure_filter(struct ieee80211_hw *hw, | ||||
| 			unsigned int changed_flags, | ||||
| @ -451,6 +421,29 @@ static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 			u32 changes) | ||||
| { | ||||
| 	struct lbtf_private *priv = hw->priv; | ||||
| 	struct sk_buff *beacon; | ||||
| 
 | ||||
| 	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { | ||||
| 		switch (priv->vif->type) { | ||||
| 		case NL80211_IFTYPE_AP: | ||||
| 		case NL80211_IFTYPE_MESH_POINT: | ||||
| 			beacon = ieee80211_beacon_get(hw, vif); | ||||
| 			if (beacon) { | ||||
| 				lbtf_beacon_set(priv, beacon); | ||||
| 				kfree_skb(beacon); | ||||
| 				lbtf_beacon_ctrl(priv, 1, | ||||
| 						 bss_conf->beacon_int); | ||||
| 			} | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_BSSID) { | ||||
| 		bool activate = !is_zero_ether_addr(bss_conf->bssid); | ||||
| 		lbtf_set_bssid(priv, activate, bss_conf->bssid); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_ERP_PREAMBLE) { | ||||
| 		if (bss_conf->use_short_preamble) | ||||
| @ -459,8 +452,6 @@ static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 			priv->preamble = CMD_TYPE_LONG_PREAMBLE; | ||||
| 		lbtf_set_radio_control(priv); | ||||
| 	} | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static const struct ieee80211_ops lbtf_ops = { | ||||
| @ -470,7 +461,6 @@ static const struct ieee80211_ops lbtf_ops = { | ||||
| 	.add_interface		= lbtf_op_add_interface, | ||||
| 	.remove_interface	= lbtf_op_remove_interface, | ||||
| 	.config			= lbtf_op_config, | ||||
| 	.config_interface	= lbtf_op_config_interface, | ||||
| 	.configure_filter	= lbtf_op_configure_filter, | ||||
| 	.bss_info_changed	= lbtf_op_bss_info_changed, | ||||
| }; | ||||
|  | ||||
| @ -553,18 +553,15 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 	struct mac80211_hwsim_data *data = hw->priv; | ||||
| 	struct ieee80211_conf *conf = &hw->conf; | ||||
| 
 | ||||
| 	printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d beacon_int=%d)\n", | ||||
| 	printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d idle=%d ps=%d)\n", | ||||
| 	       wiphy_name(hw->wiphy), __func__, | ||||
| 	       conf->channel->center_freq, conf->radio_enabled, | ||||
| 	       conf->beacon_int); | ||||
| 	       !!(conf->flags & IEEE80211_CONF_IDLE), | ||||
| 	       !!(conf->flags & IEEE80211_CONF_PS)); | ||||
| 
 | ||||
| 	data->channel = conf->channel; | ||||
| 	data->radio_enabled = conf->radio_enabled; | ||||
| 	data->beacon_int = 1024 * conf->beacon_int / 1000 * HZ / 1000; | ||||
| 	if (data->beacon_int < 1) | ||||
| 		data->beacon_int = 1; | ||||
| 
 | ||||
| 	if (!data->started || !data->radio_enabled) | ||||
| 	if (!data->started || !data->radio_enabled || !data->beacon_int) | ||||
| 		del_timer(&data->beacon_timer); | ||||
| 	else | ||||
| 		mod_timer(&data->beacon_timer, jiffies + data->beacon_int); | ||||
| @ -592,35 +589,26 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, | ||||
| 	*total_flags = data->rx_filter; | ||||
| } | ||||
| 
 | ||||
| static int mac80211_hwsim_config_interface(struct ieee80211_hw *hw, | ||||
| 					   struct ieee80211_vif *vif, | ||||
| 					   struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||||
| 
 | ||||
| 	hwsim_check_magic(vif); | ||||
| 	if (conf->changed & IEEE80211_IFCC_BSSID) { | ||||
| 		DECLARE_MAC_BUF(mac); | ||||
| 		printk(KERN_DEBUG "%s:%s: BSSID changed: %pM\n", | ||||
| 		       wiphy_name(hw->wiphy), __func__, | ||||
| 		       conf->bssid); | ||||
| 		memcpy(vp->bssid, conf->bssid, ETH_ALEN); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 					    struct ieee80211_vif *vif, | ||||
| 					    struct ieee80211_bss_conf *info, | ||||
| 					    u32 changed) | ||||
| { | ||||
| 	struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||||
| 	struct mac80211_hwsim_data *data = hw->priv; | ||||
| 
 | ||||
| 	hwsim_check_magic(vif); | ||||
| 
 | ||||
| 	printk(KERN_DEBUG "%s:%s(changed=0x%x)\n", | ||||
| 	       wiphy_name(hw->wiphy), __func__, changed); | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		printk(KERN_DEBUG "%s:%s: BSSID changed: %pM\n", | ||||
| 		       wiphy_name(hw->wiphy), __func__, | ||||
| 		       info->bssid); | ||||
| 		memcpy(vp->bssid, info->bssid, ETH_ALEN); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ASSOC) { | ||||
| 		printk(KERN_DEBUG "  %s: ASSOC: assoc=%d aid=%d\n", | ||||
| 		       wiphy_name(hw->wiphy), info->assoc, info->aid); | ||||
| @ -628,6 +616,14 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 		vp->aid = info->aid; | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BEACON_INT) { | ||||
| 		printk(KERN_DEBUG "  %s: BCNINT: %d\n", | ||||
| 		       wiphy_name(hw->wiphy), info->beacon_int); | ||||
| 		data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000; | ||||
| 		if (WARN_ON(!data->beacon_int)) | ||||
| 			data->beacon_int = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ERP_CTS_PROT) { | ||||
| 		printk(KERN_DEBUG "  %s: ERP_CTS_PROT: %d\n", | ||||
| 		       wiphy_name(hw->wiphy), info->use_cts_prot); | ||||
| @ -704,7 +700,6 @@ static const struct ieee80211_ops mac80211_hwsim_ops = | ||||
| 	.remove_interface = mac80211_hwsim_remove_interface, | ||||
| 	.config = mac80211_hwsim_config, | ||||
| 	.configure_filter = mac80211_hwsim_configure_filter, | ||||
| 	.config_interface = mac80211_hwsim_config_interface, | ||||
| 	.bss_info_changed = mac80211_hwsim_bss_info_changed, | ||||
| 	.sta_notify = mac80211_hwsim_sta_notify, | ||||
| 	.set_tim = mac80211_hwsim_set_tim, | ||||
|  | ||||
| @ -3089,19 +3089,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 	return rc ? -EINVAL : 0; | ||||
| } | ||||
| 
 | ||||
| static int mwl8k_config_interface(struct ieee80211_hw *hw, | ||||
| 				  struct ieee80211_vif *vif, | ||||
| 				  struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); | ||||
| 	u32 changed = conf->changed; | ||||
| 
 | ||||
| 	if (changed & IEEE80211_IFCC_BSSID) | ||||
| 		memcpy(mv_vif->bssid, conf->bssid, IEEE80211_ADDR_LEN); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct mwl8k_bss_info_changed_worker { | ||||
| 	struct mwl8k_work_struct header; | ||||
| 	struct ieee80211_vif *vif; | ||||
| @ -3183,8 +3170,12 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, | ||||
| { | ||||
| 	struct mwl8k_bss_info_changed_worker *worker; | ||||
| 	struct mwl8k_priv *priv = hw->priv; | ||||
| 	struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); | ||||
| 	int rc; | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) | ||||
| 		memcpy(mv_vif->bssid, info->bssid, IEEE80211_ADDR_LEN); | ||||
| 
 | ||||
| 	if ((changed & BSS_CHANGED_ASSOC) == 0) | ||||
| 		return; | ||||
| 
 | ||||
| @ -3442,7 +3433,6 @@ static const struct ieee80211_ops mwl8k_ops = { | ||||
| 	.add_interface		= mwl8k_add_interface, | ||||
| 	.remove_interface	= mwl8k_remove_interface, | ||||
| 	.config			= mwl8k_config, | ||||
| 	.config_interface	= mwl8k_config_interface, | ||||
| 	.bss_info_changed	= mwl8k_bss_info_changed, | ||||
| 	.configure_filter	= mwl8k_configure_filter, | ||||
| 	.set_rts_threshold	= mwl8k_set_rts_threshold, | ||||
|  | ||||
| @ -189,10 +189,10 @@ struct p54_common { | ||||
| 	unsigned long *used_rxkeys; | ||||
| 
 | ||||
| 	/* LED management */ | ||||
| #ifdef CONFIG_MAC80211_LEDS | ||||
| #ifdef CONFIG_P54_LEDS | ||||
| 	struct p54_led_dev leds[4]; | ||||
| 	struct delayed_work led_work; | ||||
| #endif /* CONFIG_MAC80211_LEDS */ | ||||
| #endif /* CONFIG_P54_LEDS */ | ||||
| 	u16 softled_state;		/* bit field of glowing LEDs */ | ||||
| 
 | ||||
| 	/* statistics */ | ||||
|  | ||||
| @ -822,7 +822,6 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 	struct ieee80211_tx_info *info; | ||||
| 	struct p54_tx_info *range; | ||||
| 	unsigned long flags; | ||||
| 	u32 freed = 0, last_addr = priv->rx_start; | ||||
| 
 | ||||
| 	if (unlikely(!skb || !dev || !skb_queue_len(&priv->tx_queue))) | ||||
| 		return; | ||||
| @ -842,7 +841,6 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 
 | ||||
| 		ni = IEEE80211_SKB_CB(skb->prev); | ||||
| 		mr = (struct p54_tx_info *)ni->rate_driver_data; | ||||
| 		last_addr = mr->end_addr; | ||||
| 	} | ||||
| 	if (skb->next != (struct sk_buff *)&priv->tx_queue) { | ||||
| 		struct ieee80211_tx_info *ni; | ||||
| @ -850,16 +848,11 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 
 | ||||
| 		ni = IEEE80211_SKB_CB(skb->next); | ||||
| 		mr = (struct p54_tx_info *)ni->rate_driver_data; | ||||
| 		freed = mr->start_addr - last_addr; | ||||
| 	} else | ||||
| 		freed = priv->rx_end - last_addr; | ||||
| 	} | ||||
| 	__skb_unlink(skb, &priv->tx_queue); | ||||
| 	spin_unlock_irqrestore(&priv->tx_queue.lock, flags); | ||||
| 	dev_kfree_skb_any(skb); | ||||
| 
 | ||||
| 	if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 + | ||||
| 		     IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom) | ||||
| 		p54_wake_free_queues(dev); | ||||
| 	p54_wake_free_queues(dev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(p54_free_skb); | ||||
| 
 | ||||
| @ -893,8 +886,6 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 	struct sk_buff *entry; | ||||
| 	u32 addr = le32_to_cpu(hdr->req_id) - priv->headroom; | ||||
| 	struct p54_tx_info *range = NULL; | ||||
| 	u32 freed = 0; | ||||
| 	u32 last_addr = priv->rx_start; | ||||
| 	unsigned long flags; | ||||
| 	int count, idx; | ||||
| 
 | ||||
| @ -908,7 +899,6 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 
 | ||||
| 		range = (void *)info->rate_driver_data; | ||||
| 		if (range->start_addr != addr) { | ||||
| 			last_addr = range->end_addr; | ||||
| 			entry = entry->next; | ||||
| 			continue; | ||||
| 		} | ||||
| @ -919,11 +909,8 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 
 | ||||
| 			ni = IEEE80211_SKB_CB(entry->next); | ||||
| 			mr = (struct p54_tx_info *)ni->rate_driver_data; | ||||
| 			freed = mr->start_addr - last_addr; | ||||
| 		} else | ||||
| 			freed = priv->rx_end - last_addr; | ||||
| 		} | ||||
| 
 | ||||
| 		last_addr = range->end_addr; | ||||
| 		__skb_unlink(entry, &priv->tx_queue); | ||||
| 		spin_unlock_irqrestore(&priv->tx_queue.lock, flags); | ||||
| 
 | ||||
| @ -1010,9 +997,7 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| 	spin_unlock_irqrestore(&priv->tx_queue.lock, flags); | ||||
| 
 | ||||
| out: | ||||
| 	if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 + | ||||
| 		     IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom) | ||||
| 		p54_wake_free_queues(dev); | ||||
| 	p54_wake_free_queues(dev); | ||||
| } | ||||
| 
 | ||||
| static void p54_rx_eeprom_readback(struct ieee80211_hw *dev, | ||||
| @ -2204,41 +2189,6 @@ out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int p54_config_interface(struct ieee80211_hw *dev, | ||||
| 				struct ieee80211_vif *vif, | ||||
| 				struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct p54_common *priv = dev->priv; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	mutex_lock(&priv->conf_mutex); | ||||
| 	if (conf->changed & IEEE80211_IFCC_BSSID) { | ||||
| 		memcpy(priv->bssid, conf->bssid, ETH_ALEN); | ||||
| 		ret = p54_setup_mac(dev); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (conf->changed & IEEE80211_IFCC_BEACON) { | ||||
| 		ret = p54_scan(dev, P54_SCAN_EXIT, 0); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 		ret = p54_setup_mac(dev); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 		ret = p54_beacon_update(dev, vif); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 		ret = p54_set_edcf(dev); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&priv->conf_mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void p54_configure_filter(struct ieee80211_hw *dev, | ||||
| 				 unsigned int changed_flags, | ||||
| 				 unsigned int *total_flags, | ||||
| @ -2342,8 +2292,32 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev, | ||||
| 				 u32 changed) | ||||
| { | ||||
| 	struct p54_common *priv = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ERP_SLOT) { | ||||
| 	mutex_lock(&priv->conf_mutex); | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		memcpy(priv->bssid, info->bssid, ETH_ALEN); | ||||
| 		ret = p54_setup_mac(dev); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BEACON) { | ||||
| 		ret = p54_scan(dev, P54_SCAN_EXIT, 0); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 		ret = p54_setup_mac(dev); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 		ret = p54_beacon_update(dev, vif); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 	} | ||||
| 	/* XXX: this mimics having two callbacks... clean up */ | ||||
|  out: | ||||
| 	mutex_unlock(&priv->conf_mutex); | ||||
| 
 | ||||
| 	if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { | ||||
| 		priv->use_short_slot = info->use_short_slot; | ||||
| 		p54_set_edcf(dev); | ||||
| 	} | ||||
| @ -2364,7 +2338,6 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev, | ||||
| 			p54_setup_mac(dev); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, | ||||
| @ -2619,7 +2592,6 @@ static const struct ieee80211_ops p54_ops = { | ||||
| 	.sta_notify		= p54_sta_notify, | ||||
| 	.set_key		= p54_set_key, | ||||
| 	.config			= p54_config, | ||||
| 	.config_interface	= p54_config_interface, | ||||
| 	.bss_info_changed	= p54_bss_info_changed, | ||||
| 	.configure_filter	= p54_configure_filter, | ||||
| 	.conf_tx		= p54_conf_tx, | ||||
|  | ||||
| @ -81,6 +81,29 @@ static struct usb_device_id p54u_table[] __devinitdata = { | ||||
| 
 | ||||
| MODULE_DEVICE_TABLE(usb, p54u_table); | ||||
| 
 | ||||
| static const struct { | ||||
| 	u32 intf; | ||||
| 	enum p54u_hw_type type; | ||||
| 	char fw[FIRMWARE_NAME_MAX]; | ||||
| 	char fw_legacy[FIRMWARE_NAME_MAX]; | ||||
| 	char hw[20]; | ||||
| } p54u_fwlist[__NUM_P54U_HWTYPES] = { | ||||
| 	{ | ||||
| 		.type = P54U_NET2280, | ||||
| 		.intf = FW_LM86, | ||||
| 		.fw = "isl3886usb", | ||||
| 		.fw_legacy = "isl3890usb", | ||||
| 		.hw = "ISL3886 + net2280", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.type = P54U_3887, | ||||
| 		.intf = FW_LM87, | ||||
| 		.fw = "isl3887usb", | ||||
| 		.fw_legacy = "isl3887usb_bare", | ||||
| 		.hw = "ISL3887", | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static void p54u_rx_cb(struct urb *urb) | ||||
| { | ||||
| 	struct sk_buff *skb = (struct sk_buff *) urb->context; | ||||
| @ -125,11 +148,7 @@ static void p54u_rx_cb(struct urb *urb) | ||||
| 		} | ||||
| 		skb_reset_tail_pointer(skb); | ||||
| 		skb_trim(skb, 0); | ||||
| 		if (urb->transfer_buffer != skb_tail_pointer(skb)) { | ||||
| 			/* this should not happen */ | ||||
| 			WARN_ON(1); | ||||
| 			urb->transfer_buffer = skb_tail_pointer(skb); | ||||
| 		} | ||||
| 		urb->transfer_buffer = skb_tail_pointer(skb); | ||||
| 	} | ||||
| 	skb_queue_tail(&priv->rx_queue, skb); | ||||
| 	usb_anchor_urb(urb, &priv->submitted); | ||||
| @ -206,53 +225,6 @@ static int p54u_init_urbs(struct ieee80211_hw *dev) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| 	struct urb *addr_urb, *data_urb; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	addr_urb = usb_alloc_urb(0, GFP_ATOMIC); | ||||
| 	if (!addr_urb) | ||||
| 		return; | ||||
| 
 | ||||
| 	data_urb = usb_alloc_urb(0, GFP_ATOMIC); | ||||
| 	if (!data_urb) { | ||||
| 		usb_free_urb(addr_urb); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	usb_fill_bulk_urb(addr_urb, priv->udev, | ||||
| 			  usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | ||||
| 			  &((struct p54_hdr *)skb->data)->req_id, 4, | ||||
| 			  p54u_tx_dummy_cb, dev); | ||||
| 	usb_fill_bulk_urb(data_urb, priv->udev, | ||||
| 			  usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | ||||
| 			  skb->data, skb->len, FREE_AFTER_TX(skb) ? | ||||
| 			  p54u_tx_cb : p54u_tx_dummy_cb, skb); | ||||
| 	addr_urb->transfer_flags |= URB_ZERO_PACKET; | ||||
| 	data_urb->transfer_flags |= URB_ZERO_PACKET; | ||||
| 
 | ||||
| 	usb_anchor_urb(addr_urb, &priv->submitted); | ||||
| 	err = usb_submit_urb(addr_urb, GFP_ATOMIC); | ||||
| 	if (err) { | ||||
| 		usb_unanchor_urb(addr_urb); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	usb_anchor_urb(data_urb, &priv->submitted); | ||||
| 	err = usb_submit_urb(data_urb, GFP_ATOMIC); | ||||
| 	if (err) | ||||
| 		usb_unanchor_urb(data_urb); | ||||
| 
 | ||||
|  out: | ||||
| 	usb_free_urb(addr_urb); | ||||
| 	usb_free_urb(data_urb); | ||||
| 
 | ||||
| 	if (err) | ||||
| 		p54_free_skb(dev, skb); | ||||
| } | ||||
| 
 | ||||
| static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) | ||||
| { | ||||
| 	u32 chk = 0; | ||||
| @ -425,20 +397,16 @@ static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, | ||||
| 			    data, len, &alen, 2000); | ||||
| } | ||||
| 
 | ||||
| static const char p54u_romboot_3887[] = "~~~~"; | ||||
| static const char p54u_firmware_upload_3887[] = "<\r"; | ||||
| 
 | ||||
| static int p54u_device_reset_3887(struct ieee80211_hw *dev) | ||||
| static int p54u_device_reset(struct ieee80211_hw *dev) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| 	int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING); | ||||
| 	u8 buf[4]; | ||||
| 
 | ||||
| 	if (lock) { | ||||
| 		ret = usb_lock_device_for_reset(priv->udev, priv->intf); | ||||
| 		if (ret < 0) { | ||||
| 			dev_err(&priv->udev->dev, "(p54usb) unable to lock " | ||||
| 				" device for reset: %d\n", ret); | ||||
| 				"device for reset (%d)!\n", ret); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| @ -447,26 +415,34 @@ static int p54u_device_reset_3887(struct ieee80211_hw *dev) | ||||
| 	if (lock) | ||||
| 		usb_unlock_device(priv->udev); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 	if (ret) | ||||
| 		dev_err(&priv->udev->dev, "(p54usb) unable to reset " | ||||
| 			"device: %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 			"device (%d)!\n", ret); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static const char p54u_romboot_3887[] = "~~~~"; | ||||
| static int p54u_firmware_reset_3887(struct ieee80211_hw *dev) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| 	u8 buf[4]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	memcpy(&buf, p54u_romboot_3887, sizeof(buf)); | ||||
| 	ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, | ||||
| 			    buf, sizeof(buf)); | ||||
| 	if (ret) | ||||
| 		dev_err(&priv->udev->dev, "(p54usb) unable to jump to " | ||||
| 			"boot ROM: %d\n", ret); | ||||
| 			"boot ROM (%d)!\n", ret); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static const char p54u_firmware_upload_3887[] = "<\r"; | ||||
| static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| 	const struct firmware *fw_entry = NULL; | ||||
| 	int err, alen; | ||||
| 	u8 carry = 0; | ||||
| 	u8 *buf, *tmp; | ||||
| @ -475,51 +451,29 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) | ||||
| 	struct x2_header *hdr; | ||||
| 	unsigned long timeout; | ||||
| 
 | ||||
| 	err = p54u_firmware_reset_3887(dev); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); | ||||
| 	if (!buf) { | ||||
| 		dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware" | ||||
| 					  "upload buffer!\n"); | ||||
| 		err = -ENOMEM; | ||||
| 		goto err_bufalloc; | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	err = p54u_device_reset_3887(dev); | ||||
| 	if (err) | ||||
| 		goto err_reset; | ||||
| 
 | ||||
| 	err = request_firmware(&fw_entry, "isl3887usb", &priv->udev->dev); | ||||
| 	if (err) { | ||||
| 		dev_err(&priv->udev->dev, "p54usb: cannot find firmware " | ||||
| 					  "(isl3887usb)\n"); | ||||
| 		err = request_firmware(&fw_entry, "isl3887usb_bare", | ||||
| 			&priv->udev->dev); | ||||
| 		if (err) | ||||
| 			goto err_req_fw_failed; | ||||
| 	} | ||||
| 
 | ||||
| 	err = p54_parse_firmware(dev, fw_entry); | ||||
| 	if (err) | ||||
| 		goto err_upload_failed; | ||||
| 
 | ||||
| 	if (priv->common.fw_interface != FW_LM87) { | ||||
| 		dev_err(&priv->udev->dev, "wrong firmware, " | ||||
| 			"please get a LM87 firmware and try again.\n"); | ||||
| 		err = -EINVAL; | ||||
| 		goto err_upload_failed; | ||||
| 	} | ||||
| 
 | ||||
| 	left = block_size = min((size_t)P54U_FW_BLOCK, fw_entry->size); | ||||
| 	left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size); | ||||
| 	strcpy(buf, p54u_firmware_upload_3887); | ||||
| 	left -= strlen(p54u_firmware_upload_3887); | ||||
| 	tmp += strlen(p54u_firmware_upload_3887); | ||||
| 
 | ||||
| 	data = fw_entry->data; | ||||
| 	remains = fw_entry->size; | ||||
| 	data = priv->fw->data; | ||||
| 	remains = priv->fw->size; | ||||
| 
 | ||||
| 	hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887)); | ||||
| 	memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); | ||||
| 	hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); | ||||
| 	hdr->fw_length = cpu_to_le32(fw_entry->size); | ||||
| 	hdr->fw_length = cpu_to_le32(priv->fw->size); | ||||
| 	hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, | ||||
| 					 sizeof(u32)*2)); | ||||
| 	left -= sizeof(*hdr); | ||||
| @ -561,7 +515,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) | ||||
| 		left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); | ||||
| 	} | ||||
| 
 | ||||
| 	*((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size)); | ||||
| 	*((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data, | ||||
| 						 priv->fw->size)); | ||||
| 	err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); | ||||
| 	if (err) { | ||||
| 		dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); | ||||
| @ -612,19 +567,14 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) | ||||
| 	if (err) | ||||
| 		goto err_upload_failed; | ||||
| 
 | ||||
|   err_upload_failed: | ||||
| 	release_firmware(fw_entry); | ||||
|   err_req_fw_failed: | ||||
|   err_reset: | ||||
| err_upload_failed: | ||||
| 	kfree(buf); | ||||
|   err_bufalloc: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| 	const struct firmware *fw_entry = NULL; | ||||
| 	const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; | ||||
| 	int err, alen; | ||||
| 	void *buf; | ||||
| @ -639,33 +589,6 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	err = request_firmware(&fw_entry, "isl3886usb", &priv->udev->dev); | ||||
| 	if (err) { | ||||
| 		dev_err(&priv->udev->dev, "(p54usb) cannot find firmware " | ||||
| 					  "(isl3886usb)\n"); | ||||
| 		err = request_firmware(&fw_entry, "isl3890usb", | ||||
| 			&priv->udev->dev); | ||||
| 		if (err) { | ||||
| 			kfree(buf); | ||||
| 			return err; | ||||
| 			} | ||||
| 	} | ||||
| 
 | ||||
| 	err = p54_parse_firmware(dev, fw_entry); | ||||
| 	if (err) { | ||||
| 		kfree(buf); | ||||
| 		release_firmware(fw_entry); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (priv->common.fw_interface != FW_LM86) { | ||||
| 		dev_err(&priv->udev->dev, "wrong firmware, " | ||||
| 			"please get a LM86(USB) firmware and try again.\n"); | ||||
| 		kfree(buf); | ||||
| 		release_firmware(fw_entry); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| #define P54U_WRITE(type, addr, data) \ | ||||
| 	do {\ | ||||
| 		err = p54u_write(priv, buf, type,\ | ||||
| @ -765,8 +688,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) | ||||
| 	P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); | ||||
| 
 | ||||
| 	/* finally, we can upload firmware now! */ | ||||
| 	remains = fw_entry->size; | ||||
| 	data = fw_entry->data; | ||||
| 	remains = priv->fw->size; | ||||
| 	data = priv->fw->data; | ||||
| 	offset = ISL38XX_DEV_FIRMWARE_ADDR; | ||||
| 
 | ||||
| 	while (remains) { | ||||
| @ -875,12 +798,54 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) | ||||
| #undef P54U_WRITE | ||||
| #undef P54U_READ | ||||
| 
 | ||||
|  fail: | ||||
| 	release_firmware(fw_entry); | ||||
| fail: | ||||
| 	kfree(buf); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int p54u_load_firmware(struct ieee80211_hw *dev) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| 	int err, i; | ||||
| 
 | ||||
| 	BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); | ||||
| 
 | ||||
| 	for (i = 0; i < __NUM_P54U_HWTYPES; i++) | ||||
| 		if (p54u_fwlist[i].type == priv->hw_type) | ||||
| 			break; | ||||
| 
 | ||||
| 	if (i == __NUM_P54U_HWTYPES) | ||||
| 		return -EOPNOTSUPP; | ||||
| 
 | ||||
| 	err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev); | ||||
| 	if (err) { | ||||
| 		dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " | ||||
| 					  "(%d)!\n", p54u_fwlist[i].fw, err); | ||||
| 
 | ||||
| 		err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy, | ||||
| 				       &priv->udev->dev); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	err = p54_parse_firmware(dev, priv->fw); | ||||
| 	if (err) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (priv->common.fw_interface != p54u_fwlist[i].intf) { | ||||
| 		dev_err(&priv->udev->dev, "wrong firmware, please get " | ||||
| 			"a firmware for \"%s\" and try again.\n", | ||||
| 			p54u_fwlist[i].hw); | ||||
| 		err = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	if (err) | ||||
| 		release_firmware(priv->fw); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int p54u_open(struct ieee80211_hw *dev) | ||||
| { | ||||
| 	struct p54u_priv *priv = dev->priv; | ||||
| @ -922,6 +887,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, | ||||
| 	} | ||||
| 
 | ||||
| 	priv = dev->priv; | ||||
| 	priv->hw_type = P54U_INVALID_HW; | ||||
| 
 | ||||
| 	SET_IEEE80211_DEV(dev, &intf->dev); | ||||
| 	usb_set_intfdata(intf, dev); | ||||
| @ -953,37 +919,48 @@ static int __devinit p54u_probe(struct usb_interface *intf, | ||||
| 	priv->common.open = p54u_open; | ||||
| 	priv->common.stop = p54u_stop; | ||||
| 	if (recognized_pipes < P54U_PIPE_NUMBER) { | ||||
| #ifdef CONFIG_PM | ||||
| 		/* ISL3887 needs a full reset on resume */ | ||||
| 		udev->reset_resume = 1; | ||||
| 		err = p54u_device_reset(dev); | ||||
| #endif | ||||
| 
 | ||||
| 		priv->hw_type = P54U_3887; | ||||
| 		err = p54u_upload_firmware_3887(dev); | ||||
| 		if (priv->common.fw_interface == FW_LM87) { | ||||
| 			dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); | ||||
| 			priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); | ||||
| 			priv->common.tx = p54u_tx_lm87; | ||||
| 		} else | ||||
| 			priv->common.tx = p54u_tx_3887; | ||||
| 		dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); | ||||
| 		priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); | ||||
| 		priv->common.tx = p54u_tx_lm87; | ||||
| 		priv->upload_fw = p54u_upload_firmware_3887; | ||||
| 	} else { | ||||
| 		priv->hw_type = P54U_NET2280; | ||||
| 		dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); | ||||
| 		priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); | ||||
| 		priv->common.tx = p54u_tx_net2280; | ||||
| 		err = p54u_upload_firmware_net2280(dev); | ||||
| 		priv->upload_fw = p54u_upload_firmware_net2280; | ||||
| 	} | ||||
| 	err = p54u_load_firmware(dev); | ||||
| 	if (err) | ||||
| 		goto err_free_dev; | ||||
| 
 | ||||
| 	err = priv->upload_fw(dev); | ||||
| 	if (err) | ||||
| 		goto err_free_fw; | ||||
| 
 | ||||
| 	p54u_open(dev); | ||||
| 	err = p54_read_eeprom(dev); | ||||
| 	p54u_stop(dev); | ||||
| 	if (err) | ||||
| 		goto err_free_dev; | ||||
| 		goto err_free_fw; | ||||
| 
 | ||||
| 	err = p54_register_common(dev, &udev->dev); | ||||
| 	if (err) | ||||
| 		goto err_free_dev; | ||||
| 		goto err_free_fw; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  err_free_dev: | ||||
| err_free_fw: | ||||
| 	release_firmware(priv->fw); | ||||
| 
 | ||||
| err_free_dev: | ||||
| 	ieee80211_free_hw(dev); | ||||
| 	usb_set_intfdata(intf, NULL); | ||||
| 	usb_put_dev(udev); | ||||
| @ -1002,20 +979,64 @@ static void __devexit p54u_disconnect(struct usb_interface *intf) | ||||
| 
 | ||||
| 	priv = dev->priv; | ||||
| 	usb_put_dev(interface_to_usbdev(intf)); | ||||
| 	release_firmware(priv->fw); | ||||
| 	p54_free_common(dev); | ||||
| 	ieee80211_free_hw(dev); | ||||
| } | ||||
| 
 | ||||
| static int p54u_pre_reset(struct usb_interface *intf) | ||||
| { | ||||
| 	struct ieee80211_hw *dev = usb_get_intfdata(intf); | ||||
| 
 | ||||
| 	if (!dev) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	p54u_stop(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int p54u_resume(struct usb_interface *intf) | ||||
| { | ||||
| 	struct ieee80211_hw *dev = usb_get_intfdata(intf); | ||||
| 	struct p54u_priv *priv; | ||||
| 
 | ||||
| 	if (!dev) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	priv = dev->priv; | ||||
| 	if (unlikely(!(priv->upload_fw && priv->fw))) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return priv->upload_fw(dev); | ||||
| } | ||||
| 
 | ||||
| static int p54u_post_reset(struct usb_interface *intf) | ||||
| { | ||||
| 	struct ieee80211_hw *dev = usb_get_intfdata(intf); | ||||
| 	struct p54u_priv *priv; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = p54u_resume(intf); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	/* reinitialize old device state */ | ||||
| 	priv = dev->priv; | ||||
| 	if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED) | ||||
| 		ieee80211_restart_hw(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PM | ||||
| 
 | ||||
| static int p54u_suspend(struct usb_interface *intf, pm_message_t message) | ||||
| { | ||||
| 	return p54u_pre_reset(intf); | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_PM */ | ||||
| 
 | ||||
| static struct usb_driver p54u_driver = { | ||||
| 	.name	= "p54usb", | ||||
| 	.id_table = p54u_table, | ||||
| @ -1023,6 +1044,11 @@ static struct usb_driver p54u_driver = { | ||||
| 	.disconnect = p54u_disconnect, | ||||
| 	.pre_reset = p54u_pre_reset, | ||||
| 	.post_reset = p54u_post_reset, | ||||
| #ifdef CONFIG_PM | ||||
| 	.suspend = p54u_suspend, | ||||
| 	.resume = p54u_resume, | ||||
| 	.reset_resume = p54u_resume, | ||||
| #endif /* CONFIG_PM */ | ||||
| 	.soft_unbind = 1, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -123,18 +123,26 @@ struct p54u_rx_info { | ||||
| 	struct ieee80211_hw *dev; | ||||
| }; | ||||
| 
 | ||||
| enum p54u_hw_type { | ||||
| 	P54U_INVALID_HW, | ||||
| 	P54U_NET2280, | ||||
| 	P54U_3887, | ||||
| 
 | ||||
| 	/* keep last */ | ||||
| 	__NUM_P54U_HWTYPES, | ||||
| }; | ||||
| 
 | ||||
| struct p54u_priv { | ||||
| 	struct p54_common common; | ||||
| 	struct usb_device *udev; | ||||
| 	struct usb_interface *intf; | ||||
| 	enum { | ||||
| 		P54U_NET2280 = 0, | ||||
| 		P54U_3887 | ||||
| 	} hw_type; | ||||
| 	int (*upload_fw)(struct ieee80211_hw *dev); | ||||
| 
 | ||||
| 	enum p54u_hw_type hw_type; | ||||
| 	spinlock_t lock; | ||||
| 	struct sk_buff_head rx_queue; | ||||
| 	struct usb_anchor submitted; | ||||
| 	const struct firmware *fw; | ||||
| }; | ||||
| 
 | ||||
| #endif /* P54USB_H */ | ||||
|  | ||||
| @ -77,6 +77,20 @@ config RT73USB | ||||
| 
 | ||||
| 	  When compiled as a module, this driver will be called "rt73usb.ko". | ||||
| 
 | ||||
| config RT2800USB | ||||
| 	tristate "Ralink rt2800 (USB) support" | ||||
| 	depends on USB | ||||
| 	select RT2X00_LIB_USB | ||||
| 	select RT2X00_LIB_HT | ||||
| 	select RT2X00_LIB_FIRMWARE | ||||
| 	select RT2X00_LIB_CRYPTO | ||||
| 	select CRC_CCITT | ||||
| 	---help--- | ||||
| 	  This adds support for rt2800 wireless chipset family. | ||||
| 	  Supported chips: RT2770, RT2870 & RT3070. | ||||
| 
 | ||||
| 	  When compiled as a module, this driver will be called "rt2800usb.ko". | ||||
| 
 | ||||
| config RT2X00_LIB_PCI | ||||
| 	tristate | ||||
| 	select RT2X00_LIB | ||||
| @ -88,6 +102,9 @@ config RT2X00_LIB_USB | ||||
| config RT2X00_LIB | ||||
| 	tristate | ||||
| 
 | ||||
| config RT2X00_LIB_HT | ||||
| 	boolean | ||||
| 
 | ||||
| config RT2X00_LIB_FIRMWARE | ||||
| 	boolean | ||||
| 	select FW_LOADER | ||||
|  | ||||
| @ -8,6 +8,7 @@ rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO)	+= rt2x00crypto.o | ||||
| rt2x00lib-$(CONFIG_RT2X00_LIB_RFKILL)	+= rt2x00rfkill.o | ||||
| rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE)	+= rt2x00firmware.o | ||||
| rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS)	+= rt2x00leds.o | ||||
| rt2x00lib-$(CONFIG_RT2X00_LIB_HT)	+= rt2x00ht.o | ||||
| 
 | ||||
| obj-$(CONFIG_RT2X00_LIB)		+= rt2x00lib.o | ||||
| obj-$(CONFIG_RT2X00_LIB_PCI)		+= rt2x00pci.o | ||||
| @ -17,3 +18,4 @@ obj-$(CONFIG_RT2500PCI)			+= rt2500pci.o | ||||
| obj-$(CONFIG_RT61PCI)			+= rt61pci.o | ||||
| obj-$(CONFIG_RT2500USB)			+= rt2500usb.o | ||||
| obj-$(CONFIG_RT73USB)			+= rt73usb.o | ||||
| obj-$(CONFIG_RT2800USB)			+= rt2800usb.o | ||||
|  | ||||
| @ -1580,7 +1580,6 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = { | ||||
| 	.add_interface		= rt2x00mac_add_interface, | ||||
| 	.remove_interface	= rt2x00mac_remove_interface, | ||||
| 	.config			= rt2x00mac_config, | ||||
| 	.config_interface	= rt2x00mac_config_interface, | ||||
| 	.configure_filter	= rt2x00mac_configure_filter, | ||||
| 	.get_stats		= rt2x00mac_get_stats, | ||||
| 	.bss_info_changed	= rt2x00mac_bss_info_changed, | ||||
|  | ||||
| @ -1879,7 +1879,6 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = { | ||||
| 	.add_interface		= rt2x00mac_add_interface, | ||||
| 	.remove_interface	= rt2x00mac_remove_interface, | ||||
| 	.config			= rt2x00mac_config, | ||||
| 	.config_interface	= rt2x00mac_config_interface, | ||||
| 	.configure_filter	= rt2x00mac_configure_filter, | ||||
| 	.get_stats		= rt2x00mac_get_stats, | ||||
| 	.bss_info_changed	= rt2x00mac_bss_info_changed, | ||||
|  | ||||
| @ -1559,7 +1559,7 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) | ||||
| 	rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); | ||||
| 	rt2x00_set_chip(rt2x00dev, RT2570, value, reg); | ||||
| 
 | ||||
| 	if (!rt2x00_check_rev(&rt2x00dev->chip, 0)) { | ||||
| 	if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0)) { | ||||
| 		ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| @ -1908,7 +1908,6 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = { | ||||
| 	.add_interface		= rt2x00mac_add_interface, | ||||
| 	.remove_interface	= rt2x00mac_remove_interface, | ||||
| 	.config			= rt2x00mac_config, | ||||
| 	.config_interface	= rt2x00mac_config_interface, | ||||
| 	.configure_filter	= rt2x00mac_configure_filter, | ||||
| 	.set_key		= rt2x00mac_set_key, | ||||
| 	.get_stats		= rt2x00mac_get_stats, | ||||
|  | ||||
							
								
								
									
										3071
									
								
								drivers/net/wireless/rt2x00/rt2800usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3071
									
								
								drivers/net/wireless/rt2x00/rt2800usb.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1945
									
								
								drivers/net/wireless/rt2x00/rt2800usb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1945
									
								
								drivers/net/wireless/rt2x00/rt2800usb.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -102,6 +102,15 @@ | ||||
| #define GET_DURATION(__size, __rate)	(((__size) * 8 * 10) / (__rate)) | ||||
| #define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate)) | ||||
| 
 | ||||
| /*
 | ||||
|  * Determine the alignment requirement, | ||||
|  * to make sure the 802.11 payload is padded to a 4-byte boundrary | ||||
|  * we must determine the address of the payload and calculate the | ||||
|  * amount of bytes needed to move the data. | ||||
|  */ | ||||
| #define ALIGN_SIZE(__skb, __header) \ | ||||
| 	(  ((unsigned long)((__skb)->data + (__header))) & 3 ) | ||||
| 
 | ||||
| /*
 | ||||
|  * Standard timing and size defines. | ||||
|  * These values should follow the ieee80211 specifications. | ||||
| @ -138,6 +147,7 @@ struct rt2x00_chip { | ||||
| #define RT2561		0x0302 | ||||
| #define RT2661		0x0401 | ||||
| #define RT2571		0x1300 | ||||
| #define RT2870		0x1600 | ||||
| 
 | ||||
| 	u16 rf; | ||||
| 	u32 rev; | ||||
| @ -357,6 +367,7 @@ static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) | ||||
|  *	for @tx_power_a, @tx_power_bg and @channels. | ||||
|  * @channels: Device/chipset specific channel values (See &struct rf_channel). | ||||
|  * @channels_info: Additional information for channels (See &struct channel_info). | ||||
|  * @ht: Driver HT Capabilities (See &ieee80211_sta_ht_cap). | ||||
|  */ | ||||
| struct hw_mode_spec { | ||||
| 	unsigned int supported_bands; | ||||
| @ -370,6 +381,8 @@ struct hw_mode_spec { | ||||
| 	unsigned int num_channels; | ||||
| 	const struct rf_channel *channels; | ||||
| 	const struct channel_info *channels_info; | ||||
| 
 | ||||
| 	struct ieee80211_sta_ht_cap ht; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
| @ -590,6 +603,7 @@ enum rt2x00_flags { | ||||
| 	DRIVER_REQUIRE_SCHEDULED, | ||||
| 	DRIVER_REQUIRE_DMA, | ||||
| 	DRIVER_REQUIRE_COPY_IV, | ||||
| 	DRIVER_REQUIRE_L2PAD, | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Driver features | ||||
| @ -606,6 +620,7 @@ enum rt2x00_flags { | ||||
| 	CONFIG_EXTERNAL_LNA_BG, | ||||
| 	CONFIG_DOUBLE_ANTENNA, | ||||
| 	CONFIG_DISABLE_LINK_TUNING, | ||||
| 	CONFIG_CHANNEL_HT40, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
| @ -777,6 +792,13 @@ struct rt2x00_dev { | ||||
| 	 */ | ||||
| 	u8 freq_offset; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Calibration information (for rt2800usb & rt2800pci). | ||||
| 	 * [0] -> BW20 | ||||
| 	 * [1] -> BW40 | ||||
| 	 */ | ||||
| 	u8 calibration[2]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Low level statistics which will have | ||||
| 	 * to be kept up to date while device is running. | ||||
| @ -893,11 +915,10 @@ static inline u32 rt2x00_rev(const struct rt2x00_chip *chipset) | ||||
| 	return chipset->rev; | ||||
| } | ||||
| 
 | ||||
| static inline u16 rt2x00_check_rev(const struct rt2x00_chip *chipset, | ||||
| 				   const u32 rev) | ||||
| static inline bool rt2x00_check_rev(const struct rt2x00_chip *chipset, | ||||
| 				    const u32 mask, const u32 rev) | ||||
| { | ||||
| 	return (((chipset->rev & 0xffff0) == rev) && | ||||
| 		!!(chipset->rev & 0x0000f)); | ||||
| 	return ((chipset->rev & mask) == rev); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| @ -943,9 +964,6 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, | ||||
| void rt2x00mac_remove_interface(struct ieee80211_hw *hw, | ||||
| 				struct ieee80211_if_init_conf *conf); | ||||
| int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); | ||||
| int rt2x00mac_config_interface(struct ieee80211_hw *hw, | ||||
| 			       struct ieee80211_vif *vif, | ||||
| 			       struct ieee80211_if_conf *conf); | ||||
| void rt2x00mac_configure_filter(struct ieee80211_hw *hw, | ||||
| 				unsigned int changed_flags, | ||||
| 				unsigned int *total_flags, | ||||
|  | ||||
| @ -173,6 +173,11 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, | ||||
| 	libconf.conf = conf; | ||||
| 
 | ||||
| 	if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) { | ||||
| 		if (conf_is_ht40(conf)) | ||||
| 			__set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); | ||||
| 		else | ||||
| 			__clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); | ||||
| 
 | ||||
| 		memcpy(&libconf.rf, | ||||
| 		       &rt2x00dev->spec.channels[conf->channel->hw_value], | ||||
| 		       sizeof(libconf.rf)); | ||||
|  | ||||
| @ -65,7 +65,8 @@ void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry, | ||||
| 		__set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); | ||||
| 
 | ||||
| 	txdesc->key_idx = hw_key->hw_key_idx; | ||||
| 	txdesc->iv_offset = ieee80211_get_hdrlen_from_skb(entry->skb); | ||||
| 	txdesc->iv_offset = txdesc->header_length; | ||||
| 	txdesc->iv_len = hw_key->iv_len; | ||||
| 
 | ||||
| 	if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) | ||||
| 		__set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); | ||||
| @ -103,47 +104,44 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, | ||||
| 	return overhead; | ||||
| } | ||||
| 
 | ||||
| void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, unsigned int iv_len) | ||||
| void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) | ||||
| { | ||||
| 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); | ||||
| 	unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb); | ||||
| 
 | ||||
| 	if (unlikely(!iv_len)) | ||||
| 	if (unlikely(!txdesc->iv_len)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Copy IV/EIV data */ | ||||
| 	memcpy(skbdesc->iv, skb->data + header_length, iv_len); | ||||
| 	memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); | ||||
| } | ||||
| 
 | ||||
| void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len) | ||||
| void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) | ||||
| { | ||||
| 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); | ||||
| 	unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb); | ||||
| 
 | ||||
| 	if (unlikely(!iv_len)) | ||||
| 	if (unlikely(!txdesc->iv_len)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Copy IV/EIV data */ | ||||
| 	memcpy(skbdesc->iv, skb->data + header_length, iv_len); | ||||
| 	memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); | ||||
| 
 | ||||
| 	/* Move ieee80211 header */ | ||||
| 	memmove(skb->data + iv_len, skb->data, header_length); | ||||
| 	memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); | ||||
| 
 | ||||
| 	/* Pull buffer to correct size */ | ||||
| 	skb_pull(skb, iv_len); | ||||
| 	skb_pull(skb, txdesc->iv_len); | ||||
| 
 | ||||
| 	/* IV/EIV data has officially be stripped */ | ||||
| 	skbdesc->flags |= FRAME_DESC_IV_STRIPPED; | ||||
| 	skbdesc->flags |= SKBDESC_IV_STRIPPED; | ||||
| } | ||||
| 
 | ||||
| void rt2x00crypto_tx_insert_iv(struct sk_buff *skb) | ||||
| void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) | ||||
| { | ||||
| 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); | ||||
| 	unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb); | ||||
| 	const unsigned int iv_len = | ||||
| 	    ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); | ||||
| 
 | ||||
| 	if (!(skbdesc->flags & FRAME_DESC_IV_STRIPPED)) | ||||
| 	if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) | ||||
| 		return; | ||||
| 
 | ||||
| 	skb_push(skb, iv_len); | ||||
| @ -155,14 +153,15 @@ void rt2x00crypto_tx_insert_iv(struct sk_buff *skb) | ||||
| 	memcpy(skb->data + header_length, skbdesc->iv, iv_len); | ||||
| 
 | ||||
| 	/* IV/EIV data has returned into the frame */ | ||||
| 	skbdesc->flags &= ~FRAME_DESC_IV_STRIPPED; | ||||
| 	skbdesc->flags &= ~SKBDESC_IV_STRIPPED; | ||||
| } | ||||
| 
 | ||||
| void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align, | ||||
| void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad, | ||||
| 			       unsigned int header_length, | ||||
| 			       struct rxdone_entry_desc *rxdesc) | ||||
| { | ||||
| 	unsigned int payload_len = rxdesc->size - header_length; | ||||
| 	unsigned int align = ALIGN_SIZE(skb, header_length); | ||||
| 	unsigned int iv_len; | ||||
| 	unsigned int icv_len; | ||||
| 	unsigned int transfer = 0; | ||||
| @ -192,32 +191,48 @@ void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align, | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make room for new data, note that we increase both | ||||
| 	 * headsize and tailsize when required. The tailsize is | ||||
| 	 * only needed when ICV data needs to be inserted and | ||||
| 	 * the padding is smaller than the ICV data. | ||||
| 	 * When alignment requirements is greater than the | ||||
| 	 * ICV data we must trim the skb to the correct size | ||||
| 	 * because we need to remove the extra bytes. | ||||
| 	 * Make room for new data. There are 2 possibilities | ||||
| 	 * either the alignment is already present between | ||||
| 	 * the 802.11 header and payload. In that case we | ||||
| 	 * we have to move the header less then the iv_len | ||||
| 	 * since we can use the already available l2pad bytes | ||||
| 	 * for the iv data. | ||||
| 	 * When the alignment must be added manually we must | ||||
| 	 * move the header more then iv_len since we must | ||||
| 	 * make room for the payload move as well. | ||||
| 	 */ | ||||
| 	skb_push(skb, iv_len + align); | ||||
| 	if (align < icv_len) | ||||
| 		skb_put(skb, icv_len - align); | ||||
| 	else if (align > icv_len) | ||||
| 		skb_trim(skb, rxdesc->size + iv_len + icv_len); | ||||
| 	if (l2pad) { | ||||
| 		skb_push(skb, iv_len - align); | ||||
| 		skb_put(skb, icv_len); | ||||
| 
 | ||||
| 	/* Move ieee80211 header */ | ||||
| 	memmove(skb->data + transfer, | ||||
| 		skb->data + transfer + iv_len + align, | ||||
| 		header_length); | ||||
| 	transfer += header_length; | ||||
| 		/* Move ieee80211 header */ | ||||
| 		memmove(skb->data + transfer, | ||||
| 			skb->data + transfer + (iv_len - align), | ||||
| 			header_length); | ||||
| 		transfer += header_length; | ||||
| 	} else { | ||||
| 		skb_push(skb, iv_len + align); | ||||
| 		if (align < icv_len) | ||||
| 			skb_put(skb, icv_len - align); | ||||
| 		else if (align > icv_len) | ||||
| 			skb_trim(skb, rxdesc->size + iv_len + icv_len); | ||||
| 
 | ||||
| 		/* Move ieee80211 header */ | ||||
| 		memmove(skb->data + transfer, | ||||
| 			skb->data + transfer + iv_len + align, | ||||
| 			header_length); | ||||
| 		transfer += header_length; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Copy IV/EIV data */ | ||||
| 	memcpy(skb->data + transfer, rxdesc->iv, iv_len); | ||||
| 	transfer += iv_len; | ||||
| 
 | ||||
| 	/* Move payload */ | ||||
| 	if (align) { | ||||
| 	/*
 | ||||
| 	 * Move payload for alignment purposes. Note that | ||||
| 	 * this is only needed when no l2 padding is present. | ||||
| 	 */ | ||||
| 	if (!l2pad) { | ||||
| 		memmove(skb->data + transfer, | ||||
| 			skb->data + transfer + align, | ||||
| 			payload_len); | ||||
|  | ||||
| @ -227,6 +227,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); | ||||
| 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); | ||||
| 	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; | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -234,6 +235,12 @@ void rt2x00lib_txdone(struct queue_entry *entry, | ||||
| 	 */ | ||||
| 	rt2x00queue_unmap_skb(rt2x00dev, entry->skb); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Remove L2 padding which was added during | ||||
| 	 */ | ||||
| 	if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags)) | ||||
| 		rt2x00queue_payload_align(entry->skb, true, header_length); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If the IV/EIV data was stripped from the frame before it was | ||||
| 	 * passed to the hardware, we should now reinsert it again because | ||||
| @ -241,7 +248,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, | ||||
| 	 * frame as it was passed to us. | ||||
| 	 */ | ||||
| 	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) | ||||
| 		rt2x00crypto_tx_insert_iv(entry->skb); | ||||
| 		rt2x00crypto_tx_insert_iv(entry->skb, header_length); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Send frame to debugfs immediately, after this call is completed | ||||
| @ -316,19 +323,54 @@ void rt2x00lib_txdone(struct queue_entry *entry, | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rt2x00lib_txdone); | ||||
| 
 | ||||
| static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, | ||||
| 					struct rxdone_entry_desc *rxdesc) | ||||
| { | ||||
| 	struct ieee80211_supported_band *sband; | ||||
| 	const struct rt2x00_rate *rate; | ||||
| 	unsigned int i; | ||||
| 	int signal; | ||||
| 	int type; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * For non-HT rates the MCS value needs to contain the | ||||
| 	 * actually used rate modulation (CCK or OFDM). | ||||
| 	 */ | ||||
| 	if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) | ||||
| 		signal = RATE_MCS(rxdesc->rate_mode, rxdesc->signal); | ||||
| 	else | ||||
| 		signal = rxdesc->signal; | ||||
| 
 | ||||
| 	type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); | ||||
| 
 | ||||
| 	sband = &rt2x00dev->bands[rt2x00dev->curr_band]; | ||||
| 	for (i = 0; i < sband->n_bitrates; i++) { | ||||
| 		rate = rt2x00_get_rate(sband->bitrates[i].hw_value); | ||||
| 
 | ||||
| 		if (((type == RXDONE_SIGNAL_PLCP) && | ||||
| 		     (rate->plcp == signal)) || | ||||
| 		    ((type == RXDONE_SIGNAL_BITRATE) && | ||||
| 		      (rate->bitrate == signal)) || | ||||
| 		    ((type == RXDONE_SIGNAL_MCS) && | ||||
| 		      (rate->mcs == signal))) { | ||||
| 			return i; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	WARNING(rt2x00dev, "Frame received with unrecognized signal, " | ||||
| 		"signal=0x%.4x, type=%d.\n", signal, type); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, | ||||
| 		      struct queue_entry *entry) | ||||
| { | ||||
| 	struct rxdone_entry_desc rxdesc; | ||||
| 	struct sk_buff *skb; | ||||
| 	struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; | ||||
| 	struct ieee80211_supported_band *sband; | ||||
| 	const struct rt2x00_rate *rate; | ||||
| 	unsigned int header_length; | ||||
| 	unsigned int align; | ||||
| 	unsigned int i; | ||||
| 	int idx = -1; | ||||
| 
 | ||||
| 	bool l2pad; | ||||
| 	int rate_idx; | ||||
| 	/*
 | ||||
| 	 * Allocate a new sk_buffer. If no new buffer available, drop the | ||||
| 	 * received frame and reuse the existing buffer. | ||||
| @ -348,12 +390,15 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, | ||||
| 	memset(&rxdesc, 0, sizeof(rxdesc)); | ||||
| 	rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); | ||||
| 
 | ||||
| 	/* Trim buffer to correct size */ | ||||
| 	skb_trim(entry->skb, rxdesc.size); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The data behind the ieee80211 header must be | ||||
| 	 * aligned on a 4 byte boundary. | ||||
| 	 */ | ||||
| 	header_length = ieee80211_get_hdrlen_from_skb(entry->skb); | ||||
| 	align = ((unsigned long)(entry->skb->data + header_length)) & 3; | ||||
| 	l2pad = !!(rxdesc.dev_flags & RXDONE_L2PAD); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Hardware might have stripped the IV/EIV/ICV data, | ||||
| @ -362,40 +407,24 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, | ||||
| 	 * in which case we should reinsert the data into the frame. | ||||
| 	 */ | ||||
| 	if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) && | ||||
| 	    (rxdesc.flags & RX_FLAG_IV_STRIPPED)) { | ||||
| 		rt2x00crypto_rx_insert_iv(entry->skb, align, | ||||
| 					  header_length, &rxdesc); | ||||
| 	} else if (align) { | ||||
| 		skb_push(entry->skb, align); | ||||
| 		/* Move entire frame in 1 command */ | ||||
| 		memmove(entry->skb->data, entry->skb->data + align, | ||||
| 			rxdesc.size); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Update data pointers, trim buffer to correct size */ | ||||
| 	skb_trim(entry->skb, rxdesc.size); | ||||
| 	    (rxdesc.flags & RX_FLAG_IV_STRIPPED)) | ||||
| 		rt2x00crypto_rx_insert_iv(entry->skb, l2pad, header_length, | ||||
| 					  &rxdesc); | ||||
| 	else | ||||
| 		rt2x00queue_payload_align(entry->skb, l2pad, header_length); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Update RX statistics. | ||||
| 	 * Check if the frame was received using HT. In that case, | ||||
| 	 * the rate is the MCS index and should be passed to mac80211 | ||||
| 	 * directly. Otherwise we need to translate the signal to | ||||
| 	 * the correct bitrate index. | ||||
| 	 */ | ||||
| 	sband = &rt2x00dev->bands[rt2x00dev->curr_band]; | ||||
| 	for (i = 0; i < sband->n_bitrates; i++) { | ||||
| 		rate = rt2x00_get_rate(sband->bitrates[i].hw_value); | ||||
| 
 | ||||
| 		if (((rxdesc.dev_flags & RXDONE_SIGNAL_PLCP) && | ||||
| 		     (rate->plcp == rxdesc.signal)) || | ||||
| 		    ((rxdesc.dev_flags & RXDONE_SIGNAL_BITRATE) && | ||||
| 		      (rate->bitrate == rxdesc.signal))) { | ||||
| 			idx = i; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (idx < 0) { | ||||
| 		WARNING(rt2x00dev, "Frame received with unrecognized signal," | ||||
| 			"signal=0x%.2x, type=%d.\n", rxdesc.signal, | ||||
| 			(rxdesc.dev_flags & RXDONE_SIGNAL_MASK)); | ||||
| 		idx = 0; | ||||
| 	if (rxdesc.rate_mode == RATE_MODE_CCK || | ||||
| 	    rxdesc.rate_mode == RATE_MODE_OFDM) { | ||||
| 		rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); | ||||
| 	} else { | ||||
| 		rxdesc.flags |= RX_FLAG_HT; | ||||
| 		rate_idx = rxdesc.signal; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -405,7 +434,7 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, | ||||
| 	rt2x00debug_update_crypto(rt2x00dev, &rxdesc); | ||||
| 
 | ||||
| 	rx_status->mactime = rxdesc.timestamp; | ||||
| 	rx_status->rate_idx = idx; | ||||
| 	rx_status->rate_idx = rate_idx; | ||||
| 	rx_status->qual = rt2x00link_calculate_signal(rt2x00dev, rxdesc.rssi); | ||||
| 	rx_status->signal = rxdesc.rssi; | ||||
| 	rx_status->noise = rxdesc.noise; | ||||
| @ -440,72 +469,84 @@ const struct rt2x00_rate rt2x00_supported_rates[12] = { | ||||
| 		.bitrate = 10, | ||||
| 		.ratemask = BIT(0), | ||||
| 		.plcp = 0x00, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_CCK, 0), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, | ||||
| 		.bitrate = 20, | ||||
| 		.ratemask = BIT(1), | ||||
| 		.plcp = 0x01, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_CCK, 1), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, | ||||
| 		.bitrate = 55, | ||||
| 		.ratemask = BIT(2), | ||||
| 		.plcp = 0x02, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_CCK, 2), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, | ||||
| 		.bitrate = 110, | ||||
| 		.ratemask = BIT(3), | ||||
| 		.plcp = 0x03, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_CCK, 3), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 60, | ||||
| 		.ratemask = BIT(4), | ||||
| 		.plcp = 0x0b, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 0), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 90, | ||||
| 		.ratemask = BIT(5), | ||||
| 		.plcp = 0x0f, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 1), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 120, | ||||
| 		.ratemask = BIT(6), | ||||
| 		.plcp = 0x0a, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 2), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 180, | ||||
| 		.ratemask = BIT(7), | ||||
| 		.plcp = 0x0e, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 3), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 240, | ||||
| 		.ratemask = BIT(8), | ||||
| 		.plcp = 0x09, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 4), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 360, | ||||
| 		.ratemask = BIT(9), | ||||
| 		.plcp = 0x0d, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 5), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 480, | ||||
| 		.ratemask = BIT(10), | ||||
| 		.plcp = 0x08, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 6), | ||||
| 	}, | ||||
| 	{ | ||||
| 		.flags = DEV_RATE_OFDM, | ||||
| 		.bitrate = 540, | ||||
| 		.ratemask = BIT(11), | ||||
| 		.plcp = 0x0c, | ||||
| 		.mcs = RATE_MCS(RATE_MODE_OFDM, 7), | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| @ -581,6 +622,8 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, | ||||
| 		rt2x00dev->bands[IEEE80211_BAND_2GHZ].bitrates = rates; | ||||
| 		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = | ||||
| 		    &rt2x00dev->bands[IEEE80211_BAND_2GHZ]; | ||||
| 		memcpy(&rt2x00dev->bands[IEEE80211_BAND_2GHZ].ht_cap, | ||||
| 		       &spec->ht, sizeof(spec->ht)); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -597,6 +640,8 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, | ||||
| 		rt2x00dev->bands[IEEE80211_BAND_5GHZ].bitrates = &rates[4]; | ||||
| 		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = | ||||
| 		    &rt2x00dev->bands[IEEE80211_BAND_5GHZ]; | ||||
| 		memcpy(&rt2x00dev->bands[IEEE80211_BAND_5GHZ].ht_cap, | ||||
| 		       &spec->ht, sizeof(spec->ht)); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  | ||||
							
								
								
									
										69
									
								
								drivers/net/wireless/rt2x00/rt2x00ht.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								drivers/net/wireless/rt2x00/rt2x00ht.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| /*
 | ||||
| 	Copyright (C) 2004 - 2009 rt2x00 SourceForge Project | ||||
| 	<http://rt2x00.serialmonkey.com>
 | ||||
| 
 | ||||
| 	This program is free software; you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation; either version 2 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	This program is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with this program; if not, write to the | ||||
| 	Free Software Foundation, Inc., | ||||
| 	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
| 	Module: rt2x00lib | ||||
| 	Abstract: rt2x00 HT specific routines. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include "rt2x00.h" | ||||
| #include "rt2x00lib.h" | ||||
| 
 | ||||
| void rt2x00ht_create_tx_descriptor(struct queue_entry *entry, | ||||
| 				   struct txentry_desc *txdesc, | ||||
| 				   const struct rt2x00_rate *hwrate) | ||||
| { | ||||
| 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); | ||||
| 	struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; | ||||
| 
 | ||||
| 	if (tx_info->control.sta) | ||||
| 		txdesc->mpdu_density = | ||||
| 		    tx_info->control.sta->ht_cap.ampdu_density; | ||||
| 	else | ||||
| 		txdesc->mpdu_density = 0; | ||||
| 
 | ||||
| 	txdesc->ba_size = 7;	/* FIXME: What value is needed? */ | ||||
| 	txdesc->stbc = 0;	/* FIXME: What value is needed? */ | ||||
| 
 | ||||
| 	txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs); | ||||
| 	if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) | ||||
| 		txdesc->mcs |= 0x08; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Convert flags | ||||
| 	 */ | ||||
| 	if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) | ||||
| 		__set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Determine HT Mix/Greenfield rate mode | ||||
| 	 */ | ||||
| 	if (txrate->flags & IEEE80211_TX_RC_MCS) | ||||
| 		txdesc->rate_mode = RATE_MODE_HT_MIX; | ||||
| 	if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) | ||||
| 		txdesc->rate_mode = RATE_MODE_HT_GREENFIELD; | ||||
| 	if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) | ||||
| 		__set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags); | ||||
| 	if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) | ||||
| 		__set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags); | ||||
| } | ||||
| @ -32,8 +32,8 @@ | ||||
|  * Interval defines | ||||
|  * Both the link tuner as the rfkill will be called once per second. | ||||
|  */ | ||||
| #define LINK_TUNE_INTERVAL	( round_jiffies_relative(HZ) ) | ||||
| #define RFKILL_POLL_INTERVAL	( 1000 ) | ||||
| #define LINK_TUNE_INTERVAL	round_jiffies_relative(HZ) | ||||
| #define RFKILL_POLL_INTERVAL	1000 | ||||
| 
 | ||||
| /*
 | ||||
|  * rt2x00_rate: Per rate device information | ||||
| @ -48,6 +48,7 @@ struct rt2x00_rate { | ||||
| 	unsigned short ratemask; | ||||
| 
 | ||||
| 	unsigned short plcp; | ||||
| 	unsigned short mcs; | ||||
| }; | ||||
| 
 | ||||
| extern const struct rt2x00_rate rt2x00_supported_rates[12]; | ||||
| @ -57,6 +58,14 @@ static inline const struct rt2x00_rate *rt2x00_get_rate(const u16 hw_value) | ||||
| 	return &rt2x00_supported_rates[hw_value & 0xff]; | ||||
| } | ||||
| 
 | ||||
| #define RATE_MCS(__mode, __mcs) \ | ||||
| 	( (((__mode) & 0x00ff) << 8) | ((__mcs) & 0x00ff) ) | ||||
| 
 | ||||
| static inline int rt2x00_get_rate_mcs(const u16 mcs_value) | ||||
| { | ||||
| 	return (mcs_value & 0x00ff); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Radio control handlers. | ||||
|  */ | ||||
| @ -112,6 +121,23 @@ void rt2x00queue_unmap_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); | ||||
|  */ | ||||
| void rt2x00queue_free_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); | ||||
| 
 | ||||
| /**
 | ||||
|  * rt2x00queue_payload_align - Align 802.11 payload to 4-byte boundary | ||||
|  * @skb: The skb to align | ||||
|  * @l2pad: Should L2 padding be used | ||||
|  * @header_length: Length of 802.11 header | ||||
|  * | ||||
|  * This function prepares the @skb to be send to the device or mac80211. | ||||
|  * If @l2pad is set to true padding will occur between the 802.11 header | ||||
|  * and payload. Otherwise the padding will be done in front of the 802.11 | ||||
|  * header. | ||||
|  * When @l2pad is set the function will check for the &SKBDESC_L2_PADDED | ||||
|  * flag in &skb_frame_desc. If that flag is set, the padding is removed | ||||
|  * and the flag cleared. Otherwise the padding is added and the flag is set. | ||||
|  */ | ||||
| void rt2x00queue_payload_align(struct sk_buff *skb, | ||||
| 			       bool l2pad, unsigned int header_length); | ||||
| 
 | ||||
| /**
 | ||||
|  * rt2x00queue_write_tx_frame - Write TX frame to hardware | ||||
|  * @queue: Queue over which the frame should be send | ||||
| @ -295,10 +321,12 @@ void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry, | ||||
| 				       struct txentry_desc *txdesc); | ||||
| unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, | ||||
| 				      struct sk_buff *skb); | ||||
| void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, unsigned int iv_len); | ||||
| void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len); | ||||
| void rt2x00crypto_tx_insert_iv(struct sk_buff *skb); | ||||
| void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align, | ||||
| void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, | ||||
| 			     struct txentry_desc *txdesc); | ||||
| void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, | ||||
| 			       struct txentry_desc *txdesc); | ||||
| void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length); | ||||
| void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad, | ||||
| 			       unsigned int header_length, | ||||
| 			       struct rxdone_entry_desc *rxdesc); | ||||
| #else | ||||
| @ -319,27 +347,42 @@ static inline unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev | ||||
| } | ||||
| 
 | ||||
| static inline void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, | ||||
| 					   unsigned int iv_len) | ||||
| 					   struct txentry_desc *txdesc) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, | ||||
| 					     unsigned int iv_len) | ||||
| 					     struct txentry_desc *txdesc) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb) | ||||
| static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, | ||||
| 					     unsigned int header_length) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, | ||||
| 					     unsigned int align, | ||||
| static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad, | ||||
| 					     unsigned int header_length, | ||||
| 					     struct rxdone_entry_desc *rxdesc) | ||||
| { | ||||
| } | ||||
| #endif /* CONFIG_RT2X00_LIB_CRYPTO */ | ||||
| 
 | ||||
| /*
 | ||||
|  * HT handlers. | ||||
|  */ | ||||
| #ifdef CONFIG_RT2X00_LIB_HT | ||||
| void rt2x00ht_create_tx_descriptor(struct queue_entry *entry, | ||||
| 				   struct txentry_desc *txdesc, | ||||
| 				   const struct rt2x00_rate *hwrate); | ||||
| #else | ||||
| static inline void rt2x00ht_create_tx_descriptor(struct queue_entry *entry, | ||||
| 						 struct txentry_desc *txdesc, | ||||
| 						 const struct rt2x00_rate *hwrate) | ||||
| { | ||||
| } | ||||
| #endif /* CONFIG_RT2X00_LIB_HT */ | ||||
| 
 | ||||
| /*
 | ||||
|  * RFkill handlers. | ||||
|  */ | ||||
|  | ||||
| @ -390,56 +390,6 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rt2x00mac_config); | ||||
| 
 | ||||
| int rt2x00mac_config_interface(struct ieee80211_hw *hw, | ||||
| 			       struct ieee80211_vif *vif, | ||||
| 			       struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct rt2x00_dev *rt2x00dev = hw->priv; | ||||
| 	struct rt2x00_intf *intf = vif_to_intf(vif); | ||||
| 	int update_bssid = 0; | ||||
| 	int status = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Mac80211 might be calling this function while we are trying | ||||
| 	 * to remove the device or perhaps suspending it. | ||||
| 	 */ | ||||
| 	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	spin_lock(&intf->lock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * conf->bssid can be NULL if coming from the internal | ||||
| 	 * beacon update routine. | ||||
| 	 */ | ||||
| 	if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) { | ||||
| 		update_bssid = 1; | ||||
| 		memcpy(&intf->bssid, conf->bssid, ETH_ALEN); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&intf->lock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Call rt2x00_config_intf() outside of the spinlock context since | ||||
| 	 * the call will sleep for USB drivers. By using the ieee80211_if_conf | ||||
| 	 * values as arguments we make keep access to rt2x00_intf thread safe | ||||
| 	 * even without the lock. | ||||
| 	 */ | ||||
| 	rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, | ||||
| 			      update_bssid ? conf->bssid : NULL); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Update the beacon. | ||||
| 	 */ | ||||
| 	if (conf->changed & (IEEE80211_IFCC_BEACON | | ||||
| 			     IEEE80211_IFCC_BEACON_ENABLED)) | ||||
| 		status = rt2x00queue_update_beacon(rt2x00dev, vif, | ||||
| 						   conf->enable_beacon); | ||||
| 
 | ||||
| 	return status; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rt2x00mac_config_interface); | ||||
| 
 | ||||
| void rt2x00mac_configure_filter(struct ieee80211_hw *hw, | ||||
| 				unsigned int changed_flags, | ||||
| 				unsigned int *total_flags, | ||||
| @ -623,6 +573,44 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, | ||||
| 	struct rt2x00_dev *rt2x00dev = hw->priv; | ||||
| 	struct rt2x00_intf *intf = vif_to_intf(vif); | ||||
| 	unsigned int delayed = 0; | ||||
| 	int update_bssid = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Mac80211 might be calling this function while we are trying | ||||
| 	 * to remove the device or perhaps suspending it. | ||||
| 	 */ | ||||
| 	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) | ||||
| 		return; | ||||
| 
 | ||||
| 	spin_lock(&intf->lock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * conf->bssid can be NULL if coming from the internal | ||||
| 	 * beacon update routine. | ||||
| 	 */ | ||||
| 	if (changes & BSS_CHANGED_BSSID) { | ||||
| 		update_bssid = 1; | ||||
| 		memcpy(&intf->bssid, bss_conf->bssid, ETH_ALEN); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&intf->lock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Call rt2x00_config_intf() outside of the spinlock context since | ||||
| 	 * the call will sleep for USB drivers. By using the ieee80211_if_conf | ||||
| 	 * values as arguments we make keep access to rt2x00_intf thread safe | ||||
| 	 * even without the lock. | ||||
| 	 */ | ||||
| 	if (changes & BSS_CHANGED_BSSID) | ||||
| 		rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, | ||||
| 				      update_bssid ? bss_conf->bssid : NULL); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Update the beacon. | ||||
| 	 */ | ||||
| 	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) | ||||
| 		rt2x00queue_update_beacon(rt2x00dev, vif, | ||||
| 					  bss_conf->enable_beacon); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * When the association status has changed we must reset the link | ||||
|  | ||||
| @ -148,6 +148,35 @@ void rt2x00queue_free_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) | ||||
| 	dev_kfree_skb_any(skb); | ||||
| } | ||||
| 
 | ||||
| void rt2x00queue_payload_align(struct sk_buff *skb, | ||||
| 			       bool l2pad, unsigned int header_length) | ||||
| { | ||||
| 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); | ||||
| 	unsigned int frame_length = skb->len; | ||||
| 	unsigned int align = ALIGN_SIZE(skb, header_length); | ||||
| 
 | ||||
| 	if (!align) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (l2pad) { | ||||
| 		if (skbdesc->flags & SKBDESC_L2_PADDED) { | ||||
| 			/* Remove L2 padding */ | ||||
| 			memmove(skb->data + align, skb->data, header_length); | ||||
| 			skb_pull(skb, align); | ||||
| 			skbdesc->flags &= ~SKBDESC_L2_PADDED; | ||||
| 		} else { | ||||
| 			/* Add L2 padding */ | ||||
| 			skb_push(skb, align); | ||||
| 			memmove(skb->data, skb->data + align, header_length); | ||||
| 			skbdesc->flags |= SKBDESC_L2_PADDED; | ||||
| 		} | ||||
| 	} else { | ||||
| 		/* Generic payload alignment to 4-byte boundary */ | ||||
| 		skb_push(skb, align); | ||||
| 		memmove(skb->data, skb->data + align, frame_length); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void rt2x00queue_create_tx_descriptor_seq(struct queue_entry *entry, | ||||
| 						 struct txentry_desc *txdesc) | ||||
| { | ||||
| @ -258,6 +287,12 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, | ||||
| 	txdesc->cw_max = entry->queue->cw_max; | ||||
| 	txdesc->aifs = entry->queue->aifs; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Header and alignment information. | ||||
| 	 */ | ||||
| 	txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb); | ||||
| 	txdesc->l2pad = ALIGN_SIZE(entry->skb, txdesc->header_length); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check whether this frame is to be acked. | ||||
| 	 */ | ||||
| @ -326,6 +361,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, | ||||
| 	 * Apply TX descriptor handling by components | ||||
| 	 */ | ||||
| 	rt2x00crypto_create_tx_descriptor(entry, txdesc); | ||||
| 	rt2x00ht_create_tx_descriptor(entry, txdesc, hwrate); | ||||
| 	rt2x00queue_create_tx_descriptor_seq(entry, txdesc); | ||||
| 	rt2x00queue_create_tx_descriptor_plcp(entry, txdesc, hwrate); | ||||
| } | ||||
| @ -368,7 +404,6 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) | ||||
| 	struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); | ||||
| 	struct txentry_desc txdesc; | ||||
| 	struct skb_frame_desc *skbdesc; | ||||
| 	unsigned int iv_len = 0; | ||||
| 	u8 rate_idx, rate_flags; | ||||
| 
 | ||||
| 	if (unlikely(rt2x00queue_full(queue))) | ||||
| @ -390,9 +425,6 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) | ||||
| 	entry->skb = skb; | ||||
| 	rt2x00queue_create_tx_descriptor(entry, &txdesc); | ||||
| 
 | ||||
| 	if (IEEE80211_SKB_CB(skb)->control.hw_key != NULL) | ||||
| 		iv_len = IEEE80211_SKB_CB(skb)->control.hw_key->iv_len; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All information is retrieved from the skb->cb array, | ||||
| 	 * now we should claim ownership of the driver part of that | ||||
| @ -415,11 +447,15 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) | ||||
| 	if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && | ||||
| 	    !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { | ||||
| 		if (test_bit(DRIVER_REQUIRE_COPY_IV, &queue->rt2x00dev->flags)) | ||||
| 			rt2x00crypto_tx_copy_iv(skb, iv_len); | ||||
| 			rt2x00crypto_tx_copy_iv(skb, &txdesc); | ||||
| 		else | ||||
| 			rt2x00crypto_tx_remove_iv(skb, iv_len); | ||||
| 			rt2x00crypto_tx_remove_iv(skb, &txdesc); | ||||
| 	} | ||||
| 
 | ||||
| 	if (test_bit(DRIVER_REQUIRE_L2PAD, &queue->rt2x00dev->flags)) | ||||
| 		rt2x00queue_payload_align(entry->skb, true, | ||||
| 					  txdesc.header_length); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * It could be possible that the queue was corrupted and this | ||||
| 	 * call failed. Since we always return NETDEV_TX_OK to mac80211, | ||||
|  | ||||
| @ -35,9 +35,12 @@ | ||||
|  * for USB devices this restriction does not apply, but the value of | ||||
|  * 2432 makes sense since it is big enough to contain the maximum fragment | ||||
|  * size according to the ieee802.11 specs. | ||||
|  * The aggregation size depends on support from the driver, but should | ||||
|  * be something around 3840 bytes. | ||||
|  */ | ||||
| #define DATA_FRAME_SIZE	2432 | ||||
| #define MGMT_FRAME_SIZE	256 | ||||
| #define DATA_FRAME_SIZE		2432 | ||||
| #define MGMT_FRAME_SIZE		256 | ||||
| #define AGGREGATION_SIZE	3840 | ||||
| 
 | ||||
| /**
 | ||||
|  * DOC: Number of entries per queue | ||||
| @ -87,13 +90,16 @@ enum data_queue_qid { | ||||
|  * | ||||
|  * @SKBDESC_DMA_MAPPED_RX: &skb_dma field has been mapped for RX | ||||
|  * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX | ||||
|  * @FRAME_DESC_IV_STRIPPED: Frame contained a IV/EIV provided by | ||||
|  * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by | ||||
|  *	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. | ||||
|  */ | ||||
| enum skb_frame_desc_flags { | ||||
| 	SKBDESC_DMA_MAPPED_RX = 1 << 0, | ||||
| 	SKBDESC_DMA_MAPPED_TX = 1 << 1, | ||||
| 	FRAME_DESC_IV_STRIPPED = 1 << 2, | ||||
| 	SKBDESC_IV_STRIPPED = 1 << 2, | ||||
| 	SKBDESC_L2_PADDED = 1 << 3 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
| @ -145,16 +151,20 @@ static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb) | ||||
|  * | ||||
|  * @RXDONE_SIGNAL_PLCP: Signal field contains the plcp value. | ||||
|  * @RXDONE_SIGNAL_BITRATE: Signal field contains the bitrate value. | ||||
|  * @RXDONE_SIGNAL_MCS: Signal field contains the mcs value. | ||||
|  * @RXDONE_MY_BSS: Does this frame originate from device's BSS. | ||||
|  * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data. | ||||
|  * @RXDONE_CRYPTO_ICV: Driver provided ICV data. | ||||
|  * @RXDONE_L2PAD: 802.11 payload has been padded to 4-byte boundary. | ||||
|  */ | ||||
| enum rxdone_entry_desc_flags { | ||||
| 	RXDONE_SIGNAL_PLCP = 1 << 0, | ||||
| 	RXDONE_SIGNAL_BITRATE = 1 << 1, | ||||
| 	RXDONE_MY_BSS = 1 << 2, | ||||
| 	RXDONE_CRYPTO_IV = 1 << 3, | ||||
| 	RXDONE_CRYPTO_ICV = 1 << 4, | ||||
| 	RXDONE_SIGNAL_PLCP = BIT(0), | ||||
| 	RXDONE_SIGNAL_BITRATE = BIT(1), | ||||
| 	RXDONE_SIGNAL_MCS = BIT(2), | ||||
| 	RXDONE_MY_BSS = BIT(3), | ||||
| 	RXDONE_CRYPTO_IV = BIT(4), | ||||
| 	RXDONE_CRYPTO_ICV = BIT(5), | ||||
| 	RXDONE_L2PAD = BIT(6), | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
| @ -163,7 +173,7 @@ enum rxdone_entry_desc_flags { | ||||
|  * from &rxdone_entry_desc to a signal value type. | ||||
|  */ | ||||
| #define RXDONE_SIGNAL_MASK \ | ||||
|        ( RXDONE_SIGNAL_PLCP | RXDONE_SIGNAL_BITRATE ) | ||||
| 	( RXDONE_SIGNAL_PLCP | RXDONE_SIGNAL_BITRATE | RXDONE_SIGNAL_MCS ) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct rxdone_entry_desc: RX Entry descriptor | ||||
| @ -177,6 +187,7 @@ enum rxdone_entry_desc_flags { | ||||
|  * @size: Data size of the received frame. | ||||
|  * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags). | ||||
|  * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags). | ||||
|  * @rate_mode: Rate mode (See @enum rate_modulation). | ||||
|  * @cipher: Cipher type used during decryption. | ||||
|  * @cipher_status: Decryption status. | ||||
|  * @iv: IV/EIV data used during decryption. | ||||
| @ -190,6 +201,7 @@ struct rxdone_entry_desc { | ||||
| 	int size; | ||||
| 	int flags; | ||||
| 	int dev_flags; | ||||
| 	u16 rate_mode; | ||||
| 	u8 cipher; | ||||
| 	u8 cipher_status; | ||||
| 
 | ||||
| @ -243,6 +255,9 @@ struct txdone_entry_desc { | ||||
|  * @ENTRY_TXD_ENCRYPT_PAIRWISE: Use pairwise key table (instead of shared). | ||||
|  * @ENTRY_TXD_ENCRYPT_IV: Generate IV/EIV in hardware. | ||||
|  * @ENTRY_TXD_ENCRYPT_MMIC: Generate MIC in hardware. | ||||
|  * @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU. | ||||
|  * @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth. | ||||
|  * @ENTRY_TXD_HT_SHORT_GI: Use short GI. | ||||
|  */ | ||||
| enum txentry_desc_flags { | ||||
| 	ENTRY_TXD_RTS_FRAME, | ||||
| @ -258,6 +273,9 @@ enum txentry_desc_flags { | ||||
| 	ENTRY_TXD_ENCRYPT_PAIRWISE, | ||||
| 	ENTRY_TXD_ENCRYPT_IV, | ||||
| 	ENTRY_TXD_ENCRYPT_MMIC, | ||||
| 	ENTRY_TXD_HT_AMPDU, | ||||
| 	ENTRY_TXD_HT_BW_40, | ||||
| 	ENTRY_TXD_HT_SHORT_GI, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
| @ -267,11 +285,17 @@ enum txentry_desc_flags { | ||||
|  * | ||||
|  * @flags: Descriptor flags (See &enum queue_entry_flags). | ||||
|  * @queue: Queue identification (See &enum data_queue_qid). | ||||
|  * @header_length: Length of 802.11 header. | ||||
|  * @l2pad: Amount of padding to align 802.11 payload to 4-byte boundrary. | ||||
|  * @length_high: PLCP length high word. | ||||
|  * @length_low: PLCP length low word. | ||||
|  * @signal: PLCP signal. | ||||
|  * @service: PLCP service. | ||||
|  * @msc: MCS. | ||||
|  * @stbc: STBC. | ||||
|  * @ba_size: BA size. | ||||
|  * @rate_mode: Rate mode (See @enum rate_modulation). | ||||
|  * @mpdu_density: MDPU density. | ||||
|  * @retry_limit: Max number of retries. | ||||
|  * @aifs: AIFS value. | ||||
|  * @ifs: IFS value. | ||||
| @ -280,18 +304,26 @@ enum txentry_desc_flags { | ||||
|  * @cipher: Cipher type used for encryption. | ||||
|  * @key_idx: Key index used for encryption. | ||||
|  * @iv_offset: Position where IV should be inserted by hardware. | ||||
|  * @iv_len: Length of IV data. | ||||
|  */ | ||||
| struct txentry_desc { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	enum data_queue_qid queue; | ||||
| 
 | ||||
| 	u16 header_length; | ||||
| 	u16 l2pad; | ||||
| 
 | ||||
| 	u16 length_high; | ||||
| 	u16 length_low; | ||||
| 	u16 signal; | ||||
| 	u16 service; | ||||
| 
 | ||||
| 	u16 mcs; | ||||
| 	u16 stbc; | ||||
| 	u16 ba_size; | ||||
| 	u16 rate_mode; | ||||
| 	u16 mpdu_density; | ||||
| 
 | ||||
| 	short retry_limit; | ||||
| 	short aifs; | ||||
| @ -302,6 +334,7 @@ struct txentry_desc { | ||||
| 	enum cipher cipher; | ||||
| 	u16 key_idx; | ||||
| 	u16 iv_offset; | ||||
| 	u16 iv_len; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
| @ -2735,7 +2735,6 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { | ||||
| 	.add_interface		= rt2x00mac_add_interface, | ||||
| 	.remove_interface	= rt2x00mac_remove_interface, | ||||
| 	.config			= rt2x00mac_config, | ||||
| 	.config_interface	= rt2x00mac_config_interface, | ||||
| 	.configure_filter	= rt2x00mac_configure_filter, | ||||
| 	.set_key		= rt2x00mac_set_key, | ||||
| 	.get_stats		= rt2x00mac_get_stats, | ||||
|  | ||||
| @ -1846,7 +1846,8 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) | ||||
| 	rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); | ||||
| 	rt2x00_set_chip(rt2x00dev, RT2571, value, reg); | ||||
| 
 | ||||
| 	if (!rt2x00_check_rev(&rt2x00dev->chip, 0x25730)) { | ||||
| 	if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0x25730) || | ||||
| 	    !rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) { | ||||
| 		ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| @ -2259,7 +2260,6 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = { | ||||
| 	.add_interface		= rt2x00mac_add_interface, | ||||
| 	.remove_interface	= rt2x00mac_remove_interface, | ||||
| 	.config			= rt2x00mac_config, | ||||
| 	.config_interface	= rt2x00mac_config_interface, | ||||
| 	.configure_filter	= rt2x00mac_configure_filter, | ||||
| 	.set_key		= rt2x00mac_set_key, | ||||
| 	.get_stats		= rt2x00mac_get_stats, | ||||
|  | ||||
| @ -702,30 +702,26 @@ static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int rtl8180_config_interface(struct ieee80211_hw *dev, | ||||
| 				    struct ieee80211_vif *vif, | ||||
| 				    struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct rtl8180_priv *priv = dev->priv; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < ETH_ALEN; i++) | ||||
| 		rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); | ||||
| 
 | ||||
| 	if (is_valid_ether_addr(conf->bssid)) | ||||
| 		rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA); | ||||
| 	else | ||||
| 		rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, | ||||
| 				     struct ieee80211_vif *vif, | ||||
| 				     struct ieee80211_bss_conf *info, | ||||
| 				     u32 changed) | ||||
| { | ||||
| 	struct rtl8180_priv *priv = dev->priv; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		for (i = 0; i < ETH_ALEN; i++) | ||||
| 			rtl818x_iowrite8(priv, &priv->map->BSSID[i], | ||||
| 					 info->bssid[i]); | ||||
| 
 | ||||
| 		if (is_valid_ether_addr(info->bssid)) | ||||
| 			rtl818x_iowrite8(priv, &priv->map->MSR, | ||||
| 					 RTL818X_MSR_INFRA); | ||||
| 		else | ||||
| 			rtl818x_iowrite8(priv, &priv->map->MSR, | ||||
| 					 RTL818X_MSR_NO_LINK); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) | ||||
| 	        priv->rf->conf_erp(dev, info); | ||||
| @ -770,7 +766,6 @@ static const struct ieee80211_ops rtl8180_ops = { | ||||
| 	.add_interface		= rtl8180_add_interface, | ||||
| 	.remove_interface	= rtl8180_remove_interface, | ||||
| 	.config			= rtl8180_config, | ||||
| 	.config_interface	= rtl8180_config_interface, | ||||
| 	.bss_info_changed	= rtl8180_bss_info_changed, | ||||
| 	.configure_filter	= rtl8180_configure_filter, | ||||
| }; | ||||
|  | ||||
| @ -1090,32 +1090,6 @@ static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int rtl8187_config_interface(struct ieee80211_hw *dev, | ||||
| 				    struct ieee80211_vif *vif, | ||||
| 				    struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct rtl8187_priv *priv = dev->priv; | ||||
| 	int i; | ||||
| 	u8 reg; | ||||
| 
 | ||||
| 	mutex_lock(&priv->conf_mutex); | ||||
| 	for (i = 0; i < ETH_ALEN; i++) | ||||
| 		rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); | ||||
| 
 | ||||
| 	if (is_valid_ether_addr(conf->bssid)) { | ||||
| 		reg = RTL818X_MSR_INFRA; | ||||
| 		if (priv->is_rtl8187b) | ||||
| 			reg |= RTL818X_MSR_ENEDCA; | ||||
| 		rtl818x_iowrite8(priv, &priv->map->MSR, reg); | ||||
| 	} else { | ||||
| 		reg = RTL818X_MSR_NO_LINK; | ||||
| 		rtl818x_iowrite8(priv, &priv->map->MSR, reg); | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&priv->conf_mutex); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for | ||||
|  * example. Thus we have to use raw values for AC_*_PARAM register addresses. | ||||
| @ -1193,6 +1167,27 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, | ||||
| 				     u32 changed) | ||||
| { | ||||
| 	struct rtl8187_priv *priv = dev->priv; | ||||
| 	int i; | ||||
| 	u8 reg; | ||||
| 
 | ||||
| 	if (changed & BSS_CHANGED_BSSID) { | ||||
| 		mutex_lock(&priv->conf_mutex); | ||||
| 		for (i = 0; i < ETH_ALEN; i++) | ||||
| 			rtl818x_iowrite8(priv, &priv->map->BSSID[i], | ||||
| 					 info->bssid[i]); | ||||
| 
 | ||||
| 		if (is_valid_ether_addr(info->bssid)) { | ||||
| 			reg = RTL818X_MSR_INFRA; | ||||
| 			if (priv->is_rtl8187b) | ||||
| 				reg |= RTL818X_MSR_ENEDCA; | ||||
| 			rtl818x_iowrite8(priv, &priv->map->MSR, reg); | ||||
| 		} else { | ||||
| 			reg = RTL818X_MSR_NO_LINK; | ||||
| 			rtl818x_iowrite8(priv, &priv->map->MSR, reg); | ||||
| 		} | ||||
| 
 | ||||
| 		mutex_unlock(&priv->conf_mutex); | ||||
| 	} | ||||
| 
 | ||||
| 	if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) | ||||
| 		rtl8187_conf_erp(priv, info->use_short_slot, | ||||
| @ -1274,7 +1269,6 @@ static const struct ieee80211_ops rtl8187_ops = { | ||||
| 	.add_interface		= rtl8187_add_interface, | ||||
| 	.remove_interface	= rtl8187_remove_interface, | ||||
| 	.config			= rtl8187_config, | ||||
| 	.config_interface	= rtl8187_config_interface, | ||||
| 	.bss_info_changed	= rtl8187_bss_info_changed, | ||||
| 	.configure_filter	= rtl8187_configure_filter, | ||||
| 	.conf_tx		= rtl8187_conf_tx | ||||
|  | ||||
							
								
								
									
										11
									
								
								drivers/net/wireless/wl12xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								drivers/net/wireless/wl12xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| config WL12XX | ||||
| 	tristate "TI wl1251/wl1271 support" | ||||
| 	depends on MAC80211 && WLAN_80211 && SPI_MASTER && EXPERIMENTAL | ||||
| 	select FW_LOADER | ||||
| 	select CRC7 | ||||
| 	---help--- | ||||
| 	  This module adds support for wireless adapters based on | ||||
| 	  TI wl1251/wl1271 chipsets. | ||||
| 
 | ||||
| 	  If you choose to build a module, it'll be called wl12xx. Say N if | ||||
| 	  unsure. | ||||
							
								
								
									
										4
									
								
								drivers/net/wireless/wl12xx/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								drivers/net/wireless/wl12xx/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| wl12xx-objs		= main.o spi.o event.o tx.o rx.o \
 | ||||
| 			  ps.o cmd.o acx.o boot.o init.o wl1251.o \
 | ||||
| 			  debugfs.o | ||||
| obj-$(CONFIG_WL12XX)	+= wl12xx.o | ||||
							
								
								
									
										689
									
								
								drivers/net/wireless/wl12xx/acx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										689
									
								
								drivers/net/wireless/wl12xx/acx.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,689 @@ | ||||
| #include "acx.h" | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/crc7.h> | ||||
| #include <linux/spi/spi.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "wl12xx_80211.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "ps.h" | ||||
| 
 | ||||
| int wl12xx_acx_frame_rates(struct wl12xx *wl, u8 ctrl_rate, u8 ctrl_mod, | ||||
| 			   u8 mgt_rate, u8 mgt_mod) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct acx_fw_gen_frame_rates rates; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx frame rates"); | ||||
| 
 | ||||
| 	rates.header.id = ACX_FW_GEN_FRAME_RATES; | ||||
| 	rates.header.len = sizeof(struct acx_fw_gen_frame_rates) - | ||||
| 		sizeof(struct acx_header); | ||||
| 
 | ||||
| 	rates.tx_ctrl_frame_rate = ctrl_rate; | ||||
| 	rates.tx_ctrl_frame_mod = ctrl_mod; | ||||
| 	rates.tx_mgt_frame_rate = mgt_rate; | ||||
| 	rates.tx_mgt_frame_mod = mgt_mod; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &rates, sizeof(rates)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("Failed to set FW rates and modulation"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int wl12xx_acx_station_id(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret, i; | ||||
| 	struct dot11_station_id mac; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx dot11_station_id"); | ||||
| 
 | ||||
| 	mac.header.id = DOT11_STATION_ID; | ||||
| 	mac.header.len = sizeof(mac) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	for (i = 0; i < ETH_ALEN; i++) | ||||
| 		mac.mac[i] = wl->mac_addr[ETH_ALEN - 1 - i]; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &mac, sizeof(mac)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_default_key(struct wl12xx *wl, u8 key_id) | ||||
| { | ||||
| 	struct acx_dot11_default_key default_key; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx dot11_default_key (%d)", key_id); | ||||
| 
 | ||||
| 	default_key.header.id = DOT11_DEFAULT_KEY; | ||||
| 	default_key.header.len = sizeof(default_key) - | ||||
| 		sizeof(struct acx_header); | ||||
| 
 | ||||
| 	default_key.id = key_id; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &default_key, sizeof(default_key)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("Couldnt set default key"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	wl->default_key = key_id; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_wake_up_conditions(struct wl12xx *wl, u8 listen_interval) | ||||
| { | ||||
| 	struct acx_wake_up_condition wake_up; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx wake up conditions"); | ||||
| 
 | ||||
| 	wake_up.header.id = ACX_WAKE_UP_CONDITIONS; | ||||
| 	wake_up.header.len = sizeof(wake_up) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	wake_up.wake_up_event = WAKE_UP_EVENT_DTIM_BITMAP; | ||||
| 	wake_up.listen_interval = listen_interval; | ||||
| 
 | ||||
| 	return wl12xx_cmd_configure(wl, &wake_up, sizeof(wake_up)); | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_sleep_auth(struct wl12xx *wl, u8 sleep_auth) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct acx_sleep_auth auth; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx sleep auth"); | ||||
| 
 | ||||
| 	auth.header.id = ACX_SLEEP_AUTH; | ||||
| 	auth.header.len = sizeof(auth) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	auth.sleep_auth = sleep_auth; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &auth, sizeof(auth)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_fw_version(struct wl12xx *wl, char *buf, size_t len) | ||||
| { | ||||
| 	struct wl12xx_command cmd; | ||||
| 	struct acx_revision *rev; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx fw rev"); | ||||
| 
 | ||||
| 	memset(&cmd, 0, sizeof(cmd)); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_interrogate(wl, ACX_FW_REV, sizeof(*rev), &cmd); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("ACX_FW_REV interrogate failed"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	rev = (struct acx_revision *) &cmd.parameters; | ||||
| 
 | ||||
| 	/* be careful with the buffer sizes */ | ||||
| 	strncpy(buf, rev->fw_version, min(len, sizeof(rev->fw_version))); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * if the firmware version string is exactly | ||||
| 	 * sizeof(rev->fw_version) long or fw_len is less than | ||||
| 	 * sizeof(rev->fw_version) it won't be null terminated | ||||
| 	 */ | ||||
| 	buf[min(len, sizeof(rev->fw_version)) - 1] = '\0'; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_tx_power(struct wl12xx *wl, int power) | ||||
| { | ||||
| 	struct acx_current_tx_power ie; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr"); | ||||
| 
 | ||||
| 	if (power < 0 || power > 25) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	memset(&ie, 0, sizeof(ie)); | ||||
| 
 | ||||
| 	ie.header.id = DOT11_CUR_TX_PWR; | ||||
| 	ie.header.len = sizeof(ie) - sizeof(struct acx_header); | ||||
| 	ie.current_tx_power = power * 10; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &ie, sizeof(ie)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("configure of tx power failed: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_feature_cfg(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_feature_config feature; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx feature cfg"); | ||||
| 
 | ||||
| 	memset(&feature, 0, sizeof(feature)); | ||||
| 
 | ||||
| 	feature.header.id = ACX_FEATURE_CFG; | ||||
| 	feature.header.len = sizeof(feature) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	/* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ | ||||
| 	feature.data_flow_options = 0; | ||||
| 	feature.options = 0; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &feature, sizeof(feature)); | ||||
| 	if (ret < 0) | ||||
| 		wl12xx_error("Couldnt set HW encryption"); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_mem_map(struct wl12xx *wl, void *mem_map, size_t len) | ||||
| { | ||||
| 	struct wl12xx_command cmd; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx mem map"); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_interrogate(wl, ACX_MEM_MAP, len, &cmd); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	else if (cmd.status != CMD_STATUS_SUCCESS) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	memcpy(mem_map, &cmd.parameters, len); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_data_path_params(struct wl12xx *wl, | ||||
| 				struct acx_data_path_params_resp *data_path) | ||||
| { | ||||
| 	struct acx_data_path_params params; | ||||
| 	struct wl12xx_command cmd; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx data path params"); | ||||
| 
 | ||||
| 	params.rx_packet_ring_chunk_size = DP_RX_PACKET_RING_CHUNK_SIZE; | ||||
| 	params.tx_packet_ring_chunk_size = DP_TX_PACKET_RING_CHUNK_SIZE; | ||||
| 
 | ||||
| 	params.rx_packet_ring_chunk_num = DP_RX_PACKET_RING_CHUNK_NUM; | ||||
| 	params.tx_packet_ring_chunk_num = DP_TX_PACKET_RING_CHUNK_NUM; | ||||
| 
 | ||||
| 	params.tx_complete_threshold = 1; | ||||
| 
 | ||||
| 	params.tx_complete_ring_depth = FW_TX_CMPLT_BLOCK_SIZE; | ||||
| 
 | ||||
| 	params.tx_complete_timeout = DP_TX_COMPLETE_TIME_OUT; | ||||
| 
 | ||||
| 	params.header.id = ACX_DATA_PATH_PARAMS; | ||||
| 	params.header.len = sizeof(params) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, ¶ms, sizeof(params)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_interrogate(wl, ACX_DATA_PATH_PARAMS, | ||||
| 				     sizeof(struct acx_data_path_params_resp), | ||||
| 				     &cmd); | ||||
| 
 | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to read data path parameters: %d", ret); | ||||
| 		return ret; | ||||
| 	} else if (cmd.status != CMD_STATUS_SUCCESS) { | ||||
| 		wl12xx_warning("data path parameter acx status failed"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(data_path, &cmd.parameters, sizeof(*data_path)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_rx_msdu_life_time(struct wl12xx *wl, u32 life_time) | ||||
| { | ||||
| 	struct rx_msdu_lifetime msdu_lifetime; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx rx msdu life time"); | ||||
| 
 | ||||
| 	msdu_lifetime.header.id = DOT11_RX_MSDU_LIFE_TIME; | ||||
| 	msdu_lifetime.header.len = sizeof(msdu_lifetime) - | ||||
| 		sizeof(struct acx_header); | ||||
| 	msdu_lifetime.lifetime = life_time; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &msdu_lifetime, sizeof(msdu_lifetime)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set rx msdu life time: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_rx_config(struct wl12xx *wl, u32 config, u32 filter) | ||||
| { | ||||
| 	struct acx_rx_config rx_config; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx rx config"); | ||||
| 
 | ||||
| 	rx_config.header.id = ACX_RX_CFG; | ||||
| 	rx_config.header.len = sizeof(rx_config) - sizeof(struct acx_header); | ||||
| 	rx_config.config_options = config; | ||||
| 	rx_config.filter_options = filter; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &rx_config, sizeof(rx_config)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set rx config: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_pd_threshold(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_packet_detection packet_detection; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx data pd threshold"); | ||||
| 
 | ||||
| 	/* FIXME: threshold value not set */ | ||||
| 	packet_detection.header.id = ACX_PD_THRESHOLD; | ||||
| 	packet_detection.header.len = sizeof(packet_detection) - | ||||
| 		sizeof(struct acx_header); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &packet_detection, | ||||
| 				   sizeof(packet_detection)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set pd threshold: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_slot(struct wl12xx *wl, enum acx_slot_type slot_time) | ||||
| { | ||||
| 	struct acx_slot slot; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx slot"); | ||||
| 
 | ||||
| 	slot.header.id = ACX_SLOT; | ||||
| 	slot.header.len = sizeof(slot) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	slot.wone_index = STATION_WONE_INDEX; | ||||
| 	slot.slot_time = slot_time; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &slot, sizeof(slot)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set slot time: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_group_address_tbl(struct wl12xx *wl) | ||||
| { | ||||
| 	struct multicast_grp_addr_start multicast; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx group address tbl"); | ||||
| 
 | ||||
| 	/* MAC filtering */ | ||||
| 	multicast.header.id = DOT11_GROUP_ADDRESS_TBL; | ||||
| 	multicast.header.len = sizeof(multicast) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	multicast.enabled = 0; | ||||
| 	multicast.num_groups = 0; | ||||
| 	memset(multicast.mac_table, 0, ADDRESS_GROUP_MAX_LEN); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &multicast, sizeof(multicast)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set group addr table: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_service_period_timeout(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_rx_timeout rx_timeout; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx service period timeout"); | ||||
| 
 | ||||
| 	/* RX timeout */ | ||||
| 	rx_timeout.header.id = ACX_SERVICE_PERIOD_TIMEOUT; | ||||
| 	rx_timeout.header.len = sizeof(rx_timeout) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	rx_timeout.ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; | ||||
| 	rx_timeout.upsd_timeout = RX_TIMEOUT_UPSD_DEF; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &rx_timeout, sizeof(rx_timeout)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set service period timeout: %d", | ||||
| 			       ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_rts_threshold(struct wl12xx *wl, u16 rts_threshold) | ||||
| { | ||||
| 	struct acx_rts_threshold rts; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx rts threshold"); | ||||
| 
 | ||||
| 	rts.header.id = DOT11_RTS_THRESHOLD; | ||||
| 	rts.header.len = sizeof(rts) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	rts.threshold = rts_threshold; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &rts, sizeof(rts)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set rts threshold: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_beacon_filter_opt(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_beacon_filter_option beacon_filter; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx beacon filter opt"); | ||||
| 
 | ||||
| 	beacon_filter.header.id = ACX_BEACON_FILTER_OPT; | ||||
| 	beacon_filter.header.len = sizeof(beacon_filter) - | ||||
| 		sizeof(struct acx_header); | ||||
| 
 | ||||
| 	beacon_filter.enable = 0; | ||||
| 	beacon_filter.max_num_beacons = 0; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &beacon_filter, sizeof(beacon_filter)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set beacon filter opt: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_beacon_filter_table(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_beacon_filter_ie_table ie_table; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx beacon filter table"); | ||||
| 
 | ||||
| 	ie_table.header.id = ACX_BEACON_FILTER_TABLE; | ||||
| 	ie_table.header.len = sizeof(ie_table) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	ie_table.num_ie = 0; | ||||
| 	memset(ie_table.table, 0, BEACON_FILTER_TABLE_MAX_SIZE); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &ie_table, sizeof(ie_table)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set beacon filter table: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_sg_enable(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_bt_wlan_coex pta; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx sg enable"); | ||||
| 
 | ||||
| 	pta.header.id = ACX_SG_ENABLE; | ||||
| 	pta.header.len = sizeof(pta) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	pta.enable = SG_ENABLE; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &pta, sizeof(pta)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set softgemini enable: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_sg_cfg(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_bt_wlan_coex_param param; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx sg cfg"); | ||||
| 
 | ||||
| 	/* BT-WLAN coext parameters */ | ||||
| 	param.header.id = ACX_SG_CFG; | ||||
| 	param.header.len = sizeof(param) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	param.min_rate = RATE_INDEX_24MBPS; | ||||
| 	param.bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; | ||||
| 	param.wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; | ||||
| 	param.sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; | ||||
| 	param.rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; | ||||
| 	param.tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; | ||||
| 	param.rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; | ||||
| 	param.tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; | ||||
| 	param.wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; | ||||
| 	param.bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; | ||||
| 	param.next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; | ||||
| 	param.wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; | ||||
| 	param.hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; | ||||
| 	param.next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; | ||||
| 	param.antenna_type = PTA_ANTENNA_TYPE_DEF; | ||||
| 	param.signal_type = PTA_SIGNALING_TYPE_DEF; | ||||
| 	param.afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; | ||||
| 	param.quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; | ||||
| 	param.max_cts = PTA_MAX_NUM_CTS_DEF; | ||||
| 	param.wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; | ||||
| 	param.bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; | ||||
| 	param.missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; | ||||
| 	param.wlan_elp_hp = PTA_ELP_HP_DEF; | ||||
| 	param.bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; | ||||
| 	param.ack_mode_dual_ant = PTA_ACK_MODE_DEF; | ||||
| 	param.pa_sd_enable = PTA_ALLOW_PA_SD_DEF; | ||||
| 	param.pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; | ||||
| 	param.bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, ¶m, sizeof(param)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set sg config: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_cca_threshold(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_energy_detection detection; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx cca threshold"); | ||||
| 
 | ||||
| 	detection.header.id = ACX_CCA_THRESHOLD; | ||||
| 	detection.header.len = sizeof(detection) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	detection.rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; | ||||
| 	detection.tx_energy_detection = 0; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &detection, sizeof(detection)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set cca threshold: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_bcn_dtim_options(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_beacon_broadcast bb; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx bcn dtim options"); | ||||
| 
 | ||||
| 	bb.header.id = ACX_BCN_DTIM_OPTIONS; | ||||
| 	bb.header.len = sizeof(bb) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	bb.beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; | ||||
| 	bb.broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; | ||||
| 	bb.rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; | ||||
| 	bb.ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &bb, sizeof(bb)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set rx config: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_aid(struct wl12xx *wl, u16 aid) | ||||
| { | ||||
| 	struct acx_aid acx_aid; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx aid"); | ||||
| 
 | ||||
| 	acx_aid.header.id = ACX_AID; | ||||
| 	acx_aid.header.len = sizeof(acx_aid) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	acx_aid.aid = aid; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &acx_aid, sizeof(acx_aid)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set aid: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_event_mbox_mask(struct wl12xx *wl, u32 event_mask) | ||||
| { | ||||
| 	struct acx_event_mask mask; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx event mbox mask"); | ||||
| 
 | ||||
| 	mask.header.id = ACX_EVENT_MBOX_MASK; | ||||
| 	mask.header.len = sizeof(mask) - sizeof(struct acx_header); | ||||
| 
 | ||||
| 	/* high event mask is unused */ | ||||
| 	mask.high_event_mask = 0xffffffff; | ||||
| 
 | ||||
| 	mask.event_mask = event_mask; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &mask, sizeof(mask)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("failed to set aid: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_set_preamble(struct wl12xx *wl, enum acx_preamble_type preamble) | ||||
| { | ||||
| 	struct acx_preamble ie; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx_set_preamble"); | ||||
| 
 | ||||
| 	memset(&ie, 0, sizeof(ie)); | ||||
| 
 | ||||
| 	ie.header.id = ACX_PREAMBLE_TYPE; | ||||
| 	ie.header.len = sizeof(ie) - sizeof(struct acx_header); | ||||
| 	ie.preamble = preamble; | ||||
| 	ret = wl12xx_cmd_configure(wl, &ie, sizeof(ie)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("Setting of preamble failed: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_cts_protect(struct wl12xx *wl, | ||||
| 			   enum acx_ctsprotect_type ctsprotect) | ||||
| { | ||||
| 	struct acx_ctsprotect ie; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx_set_ctsprotect"); | ||||
| 
 | ||||
| 	memset(&ie, 0, sizeof(ie)); | ||||
| 
 | ||||
| 	ie.header.id = ACX_CTS_PROTECTION; | ||||
| 	ie.header.len = sizeof(ie) - sizeof(struct acx_header); | ||||
| 	ie.ctsprotect = ctsprotect; | ||||
| 	ret = wl12xx_cmd_configure(wl, &ie, sizeof(ie)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("Setting of ctsprotect failed: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_acx_statistics(struct wl12xx *wl, struct acx_statistics *stats) | ||||
| { | ||||
| 	struct wl12xx_command *answer; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx statistics"); | ||||
| 
 | ||||
| 	answer = kmalloc(sizeof(*answer), GFP_KERNEL); | ||||
| 	if (!answer) { | ||||
| 		wl12xx_warning("could not allocate memory for acx statistics"); | ||||
| 		ret = -ENOMEM; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_interrogate(wl, ACX_STATISTICS, sizeof(*answer), | ||||
| 				     answer); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("acx statistics failed: %d", ret); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(stats, answer->parameters, sizeof(*stats)); | ||||
| 
 | ||||
| out: | ||||
| 	kfree(answer); | ||||
| 	return ret; | ||||
| } | ||||
							
								
								
									
										1245
									
								
								drivers/net/wireless/wl12xx/acx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1245
									
								
								drivers/net/wireless/wl12xx/acx.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										295
									
								
								drivers/net/wireless/wl12xx/boot.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								drivers/net/wireless/wl12xx/boot.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,295 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/gpio.h> | ||||
| 
 | ||||
| #include "reg.h" | ||||
| #include "boot.h" | ||||
| #include "spi.h" | ||||
| #include "event.h" | ||||
| 
 | ||||
| static void wl12xx_boot_enable_interrupts(struct wl12xx *wl) | ||||
| { | ||||
| 	enable_irq(wl->irq); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_boot_target_enable_interrupts(struct wl12xx *wl) | ||||
| { | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); | ||||
| 	wl12xx_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); | ||||
| } | ||||
| 
 | ||||
| int wl12xx_boot_soft_reset(struct wl12xx *wl) | ||||
| { | ||||
| 	unsigned long timeout; | ||||
| 	u32 boot_data; | ||||
| 
 | ||||
| 	/* perform soft reset */ | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); | ||||
| 
 | ||||
| 	/* SOFT_RESET is self clearing */ | ||||
| 	timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); | ||||
| 	while (1) { | ||||
| 		boot_data = wl12xx_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); | ||||
| 		wl12xx_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); | ||||
| 		if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) | ||||
| 			break; | ||||
| 
 | ||||
| 		if (time_after(jiffies, timeout)) { | ||||
| 			/* 1.2 check pWhalBus->uSelfClearTime if the
 | ||||
| 			 * timeout was reached */ | ||||
| 			wl12xx_error("soft reset timeout"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		udelay(SOFT_RESET_STALL_TIME); | ||||
| 	} | ||||
| 
 | ||||
| 	/* disable Rx/Tx */ | ||||
| 	wl12xx_reg_write32(wl, ENABLE, 0x0); | ||||
| 
 | ||||
| 	/* disable auto calibration on start*/ | ||||
| 	wl12xx_reg_write32(wl, SPARE_A2, 0xffff); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_boot_init_seq(struct wl12xx *wl) | ||||
| { | ||||
| 	u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * col #1: INTEGER_DIVIDER | ||||
| 	 * col #2: FRACTIONAL_DIVIDER | ||||
| 	 * col #3: ATTN_BB | ||||
| 	 * col #4: ALPHA_BB | ||||
| 	 * col #5: STOP_TIME_BB | ||||
| 	 * col #6: BB_PLL_LOOP_FILTER | ||||
| 	 */ | ||||
| 	static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = { | ||||
| 
 | ||||
| 		{   83, 87381,  0xB, 5, 0xF00,  3}, /* REF_FREQ_19_2*/ | ||||
| 		{   61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/ | ||||
| 		{   41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/ | ||||
| 		{   40, 0,      0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/ | ||||
| 		{   47, 162280, 0xC, 6, 0x2760, 1}  /* REF_FREQ_33_6        */ | ||||
| 	}; | ||||
| 
 | ||||
| 	/* read NVS params */ | ||||
| 	scr_pad6 = wl12xx_reg_read32(wl, SCR_PAD6); | ||||
| 	wl12xx_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6); | ||||
| 
 | ||||
| 	/* read ELP_CMD */ | ||||
| 	elp_cmd = wl12xx_reg_read32(wl, ELP_CMD); | ||||
| 	wl12xx_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd); | ||||
| 
 | ||||
| 	/* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */ | ||||
| 	ref_freq = scr_pad6 & 0x000000FF; | ||||
| 	wl12xx_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq); | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, PLL_CAL_TIME, 0x9); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME) | ||||
| 	 */ | ||||
| 	wl12xx_reg_write32(wl, CLK_BUF_TIME, 0x6); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * set the clock detect feature to work in the restart wu procedure | ||||
| 	 * (ELP_CFG_MODE[14]) and Select the clock source type | ||||
| 	 * (ELP_CFG_MODE[13:12]) | ||||
| 	 */ | ||||
| 	tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000; | ||||
| 	wl12xx_reg_write32(wl, ELP_CFG_MODE, tmp); | ||||
| 
 | ||||
| 	/* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */ | ||||
| 	elp_cmd |= 0x00000040; | ||||
| 	wl12xx_reg_write32(wl, ELP_CMD, elp_cmd); | ||||
| 
 | ||||
| 	/* PG 1.2: Set the BB PLL stable time to be 1000usec
 | ||||
| 	 * (PLL_STABLE_TIME) */ | ||||
| 	wl12xx_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20); | ||||
| 
 | ||||
| 	/* PG 1.2: read clock request time */ | ||||
| 	init_data = wl12xx_reg_read32(wl, CLK_REQ_TIME); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * PG 1.2: set the clock request time to be ref_clk_settling_time - | ||||
| 	 * 1ms = 4ms | ||||
| 	 */ | ||||
| 	if (init_data > 0x21) | ||||
| 		tmp = init_data - 0x21; | ||||
| 	else | ||||
| 		tmp = 0; | ||||
| 	wl12xx_reg_write32(wl, CLK_REQ_TIME, tmp); | ||||
| 
 | ||||
| 	/* set BB PLL configurations in RF AFE */ | ||||
| 	wl12xx_reg_write32(wl, 0x003058cc, 0x4B5); | ||||
| 
 | ||||
| 	/* set RF_AFE_REG_5 */ | ||||
| 	wl12xx_reg_write32(wl, 0x003058d4, 0x50); | ||||
| 
 | ||||
| 	/* set RF_AFE_CTRL_REG_2 */ | ||||
| 	wl12xx_reg_write32(wl, 0x00305948, 0x11c001); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * change RF PLL and BB PLL divider for VCO clock and adjust VCO | ||||
| 	 * bais current(RF_AFE_REG_13) | ||||
| 	 */ | ||||
| 	wl12xx_reg_write32(wl, 0x003058f4, 0x1e); | ||||
| 
 | ||||
| 	/* set BB PLL configurations */ | ||||
| 	tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000; | ||||
| 	wl12xx_reg_write32(wl, 0x00305840, tmp); | ||||
| 
 | ||||
| 	/* set fractional divider according to Appendix C-BB PLL
 | ||||
| 	 * Calculations | ||||
| 	 */ | ||||
| 	tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER]; | ||||
| 	wl12xx_reg_write32(wl, 0x00305844, tmp); | ||||
| 
 | ||||
| 	/* set the initial data for the sigma delta */ | ||||
| 	wl12xx_reg_write32(wl, 0x00305848, 0x3039); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * set the accumulator attenuation value, calibration loop1 | ||||
| 	 * (alpha), calibration loop2 (beta), calibration loop3 (gamma) and | ||||
| 	 * the VCO gain | ||||
| 	 */ | ||||
| 	tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) | | ||||
| 		(LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1; | ||||
| 	wl12xx_reg_write32(wl, 0x00305854, tmp); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * set the calibration stop time after holdoff time expires and set | ||||
| 	 * settling time HOLD_OFF_TIME_BB | ||||
| 	 */ | ||||
| 	tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000; | ||||
| 	wl12xx_reg_write32(wl, 0x00305858, tmp); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL | ||||
| 	 * constant leakage current to linearize PFD to 0uA - | ||||
| 	 * BB_ILOOPF[7:3] | ||||
| 	 */ | ||||
| 	tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030; | ||||
| 	wl12xx_reg_write32(wl, 0x003058f8, tmp); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * set regulator output voltage for n divider to | ||||
| 	 * 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2], | ||||
| 	 * set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB | ||||
| 	 * PLL auto-call to normal mode- BB_CALGAIN_3DB[8] | ||||
| 	 */ | ||||
| 	wl12xx_reg_write32(wl, 0x003058f0, 0x29); | ||||
| 
 | ||||
| 	/* enable restart wakeup sequence (ELP_CMD[0]) */ | ||||
| 	wl12xx_reg_write32(wl, ELP_CMD, elp_cmd | 0x1); | ||||
| 
 | ||||
| 	/* restart sequence completed */ | ||||
| 	udelay(2000); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_boot_run_firmware(struct wl12xx *wl) | ||||
| { | ||||
| 	int loop, ret; | ||||
| 	u32 chip_id, interrupt; | ||||
| 
 | ||||
| 	wl->chip.op_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); | ||||
| 
 | ||||
| 	chip_id = wl12xx_reg_read32(wl, CHIP_ID_B); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); | ||||
| 
 | ||||
| 	if (chip_id != wl->chip.id) { | ||||
| 		wl12xx_error("chip id doesn't match after firmware boot"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	/* wait for init to complete */ | ||||
| 	loop = 0; | ||||
| 	while (loop++ < INIT_LOOP) { | ||||
| 		udelay(INIT_LOOP_DELAY); | ||||
| 		interrupt = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); | ||||
| 
 | ||||
| 		if (interrupt == 0xffffffff) { | ||||
| 			wl12xx_error("error reading hardware complete " | ||||
| 				     "init indication"); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 		/* check that ACX_INTR_INIT_COMPLETE is enabled */ | ||||
| 		else if (interrupt & wl->chip.intr_init_complete) { | ||||
| 			wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_ACK, | ||||
| 					   wl->chip.intr_init_complete); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (loop >= INIT_LOOP) { | ||||
| 		wl12xx_error("timeout waiting for the hardware to " | ||||
| 			     "complete initialization"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	/* get hardware config command mail box */ | ||||
| 	wl->cmd_box_addr = wl12xx_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); | ||||
| 
 | ||||
| 	/* get hardware config event mail box */ | ||||
| 	wl->event_box_addr = wl12xx_reg_read32(wl, REG_EVENT_MAILBOX_PTR); | ||||
| 
 | ||||
| 	/* set the working partition to its "running" mode offset */ | ||||
| 	wl12xx_set_partition(wl, | ||||
| 			     wl->chip.p_table[PART_WORK].mem.start, | ||||
| 			     wl->chip.p_table[PART_WORK].mem.size, | ||||
| 			     wl->chip.p_table[PART_WORK].reg.start, | ||||
| 			     wl->chip.p_table[PART_WORK].reg.size); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", | ||||
| 		     wl->cmd_box_addr, wl->event_box_addr); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * in case of full asynchronous mode the firmware event must be | ||||
| 	 * ready to receive event from the command mailbox | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* enable gpio interrupts */ | ||||
| 	wl12xx_boot_enable_interrupts(wl); | ||||
| 
 | ||||
| 	wl->chip.op_target_enable_interrupts(wl); | ||||
| 
 | ||||
| 	/* unmask all mbox events  */ | ||||
| 	wl->event_mask = 0xffffffff; | ||||
| 
 | ||||
| 	ret = wl12xx_event_unmask(wl); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("EVENT mask setting failed"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_event_mbox_config(wl); | ||||
| 
 | ||||
| 	/* firmware startup completed */ | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										40
									
								
								drivers/net/wireless/wl12xx/boot.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								drivers/net/wireless/wl12xx/boot.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __BOOT_H__ | ||||
| #define __BOOT_H__ | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| 
 | ||||
| int wl12xx_boot_soft_reset(struct wl12xx *wl); | ||||
| int wl12xx_boot_init_seq(struct wl12xx *wl); | ||||
| int wl12xx_boot_run_firmware(struct wl12xx *wl); | ||||
| void wl12xx_boot_target_enable_interrupts(struct wl12xx *wl); | ||||
| 
 | ||||
| /* number of times we try to read the INIT interrupt */ | ||||
| #define INIT_LOOP 20000 | ||||
| 
 | ||||
| /* delay between retries */ | ||||
| #define INIT_LOOP_DELAY 50 | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										353
									
								
								drivers/net/wireless/wl12xx/cmd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								drivers/net/wireless/wl12xx/cmd.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,353 @@ | ||||
| #include "cmd.h" | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/crc7.h> | ||||
| #include <linux/spi/spi.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "wl12xx_80211.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "ps.h" | ||||
| 
 | ||||
| int wl12xx_cmd_send(struct wl12xx *wl, u16 type, void *buf, size_t buf_len) | ||||
| { | ||||
| 	struct wl12xx_command cmd; | ||||
| 	unsigned long timeout; | ||||
| 	size_t cmd_len; | ||||
| 	u32 intr; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	memset(&cmd, 0, sizeof(cmd)); | ||||
| 	cmd.id = type; | ||||
| 	cmd.status = 0; | ||||
| 	memcpy(cmd.parameters, buf, buf_len); | ||||
| 	cmd_len = ALIGN(buf_len, 4) + CMDMBOX_HEADER_LEN; | ||||
| 
 | ||||
| 	wl12xx_ps_elp_wakeup(wl); | ||||
| 
 | ||||
| 	wl12xx_spi_mem_write(wl, wl->cmd_box_addr, &cmd, cmd_len); | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); | ||||
| 
 | ||||
| 	timeout = jiffies + msecs_to_jiffies(WL12XX_COMMAND_TIMEOUT); | ||||
| 
 | ||||
| 	intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); | ||||
| 	while (!(intr & wl->chip.intr_cmd_complete)) { | ||||
| 		if (time_after(jiffies, timeout)) { | ||||
| 			wl12xx_error("command complete timeout"); | ||||
| 			ret = -ETIMEDOUT; | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| 		msleep(1); | ||||
| 
 | ||||
| 		intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_ACK, | ||||
| 			   wl->chip.intr_cmd_complete); | ||||
| 
 | ||||
| out: | ||||
| 	wl12xx_ps_elp_sleep(wl); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_test(struct wl12xx *wl, void *buf, size_t buf_len, u8 answer) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd test"); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_TEST, buf, buf_len); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("TEST command failed"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (answer) { | ||||
| 		struct wl12xx_command *cmd_answer; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * The test command got in, we can read the answer. | ||||
| 		 * The answer would be a wl12xx_command, where the | ||||
| 		 * parameter array contains the actual answer. | ||||
| 		 */ | ||||
| 
 | ||||
| 		wl12xx_ps_elp_wakeup(wl); | ||||
| 
 | ||||
| 		wl12xx_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len); | ||||
| 
 | ||||
| 		wl12xx_ps_elp_sleep(wl); | ||||
| 
 | ||||
| 		cmd_answer = buf; | ||||
| 		if (cmd_answer->status != CMD_STATUS_SUCCESS) | ||||
| 			wl12xx_error("TEST command answer error: %d", | ||||
| 				     cmd_answer->status); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int wl12xx_cmd_interrogate(struct wl12xx *wl, u16 ie_id, u16 ie_len, | ||||
| 			   void *answer) | ||||
| { | ||||
| 	struct wl12xx_command *cmd; | ||||
| 	struct acx_header header; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd interrogate"); | ||||
| 
 | ||||
| 	header.id = ie_id; | ||||
| 	header.len = ie_len - sizeof(header); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_INTERROGATE, &header, sizeof(header)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("INTERROGATE command failed"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_ps_elp_wakeup(wl); | ||||
| 
 | ||||
| 	/* the interrogate command got in, we can read the answer */ | ||||
| 	wl12xx_spi_mem_read(wl, wl->cmd_box_addr, answer, | ||||
| 			    CMDMBOX_HEADER_LEN + ie_len); | ||||
| 
 | ||||
| 	wl12xx_ps_elp_sleep(wl); | ||||
| 
 | ||||
| 	cmd = answer; | ||||
| 	if (cmd->status != CMD_STATUS_SUCCESS) | ||||
| 		wl12xx_error("INTERROGATE command error: %d", | ||||
| 			     cmd->status); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_configure(struct wl12xx *wl, void *ie, int ie_len) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd configure"); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_CONFIGURE, ie, | ||||
| 			      ie_len); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("CONFIGURE command NOK"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_vbm(struct wl12xx *wl, u8 identity, | ||||
| 		   void *bitmap, u16 bitmap_len, u8 bitmap_control) | ||||
| { | ||||
| 	struct vbm_update_request vbm; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd vbm"); | ||||
| 
 | ||||
| 	/* Count and period will be filled by the target */ | ||||
| 	vbm.tim.bitmap_ctrl = bitmap_control; | ||||
| 	if (bitmap_len > PARTIAL_VBM_MAX) { | ||||
| 		wl12xx_warning("cmd vbm len is %d B, truncating to %d", | ||||
| 			       bitmap_len, PARTIAL_VBM_MAX); | ||||
| 		bitmap_len = PARTIAL_VBM_MAX; | ||||
| 	} | ||||
| 	memcpy(vbm.tim.pvb_field, bitmap, bitmap_len); | ||||
| 	vbm.tim.identity = identity; | ||||
| 	vbm.tim.length = bitmap_len + 3; | ||||
| 
 | ||||
| 	vbm.len = cpu_to_le16(bitmap_len + 5); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_VBM, &vbm, sizeof(vbm)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("VBM command failed"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_data_path(struct wl12xx *wl, u8 channel, u8 enable) | ||||
| { | ||||
| 	int ret; | ||||
| 	u16 cmd_rx, cmd_tx; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd data path"); | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		cmd_rx = CMD_ENABLE_RX; | ||||
| 		cmd_tx = CMD_ENABLE_TX; | ||||
| 	} else { | ||||
| 		cmd_rx = CMD_DISABLE_RX; | ||||
| 		cmd_tx = CMD_DISABLE_TX; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, cmd_rx, &channel, sizeof(channel)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("rx %s cmd for channel %d failed", | ||||
| 			     enable ? "start" : "stop", channel); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "rx %s cmd channel %d", | ||||
| 		     enable ? "start" : "stop", channel); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, cmd_tx, &channel, sizeof(channel)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("tx %s cmd for channel %d failed", | ||||
| 			     enable ? "start" : "stop", channel); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "tx %s cmd channel %d", | ||||
| 		     enable ? "start" : "stop", channel); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_join(struct wl12xx *wl, u8 bss_type, u8 dtim_interval, | ||||
| 		    u16 beacon_interval, u8 wait) | ||||
| { | ||||
| 	unsigned long timeout; | ||||
| 	struct cmd_join join = {}; | ||||
| 	int ret, i; | ||||
| 	u8 *bssid; | ||||
| 
 | ||||
| 	/* FIXME: this should be in main.c */ | ||||
| 	ret = wl12xx_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE, | ||||
| 				     DEFAULT_HW_GEN_MODULATION_TYPE, | ||||
| 				     wl->tx_mgmt_frm_rate, | ||||
| 				     wl->tx_mgmt_frm_mod); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd join"); | ||||
| 
 | ||||
| 	/* Reverse order BSSID */ | ||||
| 	bssid = (u8 *)&join.bssid_lsb; | ||||
| 	for (i = 0; i < ETH_ALEN; i++) | ||||
| 		bssid[i] = wl->bssid[ETH_ALEN - i - 1]; | ||||
| 
 | ||||
| 	join.rx_config_options = wl->rx_config; | ||||
| 	join.rx_filter_options = wl->rx_filter; | ||||
| 
 | ||||
| 	join.basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | | ||||
| 		RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; | ||||
| 
 | ||||
| 	join.beacon_interval = beacon_interval; | ||||
| 	join.dtim_interval = dtim_interval; | ||||
| 	join.bss_type = bss_type; | ||||
| 	join.channel = wl->channel; | ||||
| 	join.ctrl = JOIN_CMD_CTRL_TX_FLUSH; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_START_JOIN, &join, sizeof(join)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("failed to initiate cmd join"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	timeout = msecs_to_jiffies(JOIN_TIMEOUT); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to | ||||
| 	 * simplify locking we just sleep instead, for now | ||||
| 	 */ | ||||
| 	if (wait) | ||||
| 		msleep(10); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_ps_mode(struct wl12xx *wl, u8 ps_mode) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct acx_ps_params ps_params; | ||||
| 
 | ||||
| 	/* FIXME: this should be in ps.c */ | ||||
| 	ret = wl12xx_acx_wake_up_conditions(wl, wl->listen_int); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("Couldnt set wake up conditions"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd set ps mode"); | ||||
| 
 | ||||
| 	ps_params.ps_mode = ps_mode; | ||||
| 	ps_params.send_null_data = 1; | ||||
| 	ps_params.retries = 5; | ||||
| 	ps_params.hang_over_period = 128; | ||||
| 	ps_params.null_data_rate = 1; /* 1 Mbps */ | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_SET_PS_MODE, &ps_params, | ||||
| 			      sizeof(ps_params)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("cmd set_ps_mode failed"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_read_memory(struct wl12xx *wl, u32 addr, u32 len, void *answer) | ||||
| { | ||||
| 	struct cmd_read_write_memory mem_cmd, *mem_answer; | ||||
| 	struct wl12xx_command cmd; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd read memory"); | ||||
| 
 | ||||
| 	memset(&mem_cmd, 0, sizeof(mem_cmd)); | ||||
| 	mem_cmd.addr = addr; | ||||
| 	mem_cmd.size = len; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, CMD_READ_MEMORY, &mem_cmd, sizeof(mem_cmd)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("read memory command failed: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	/* the read command got in, we can now read the answer */ | ||||
| 	wl12xx_spi_mem_read(wl, wl->cmd_box_addr, &cmd, | ||||
| 			    CMDMBOX_HEADER_LEN + sizeof(mem_cmd)); | ||||
| 
 | ||||
| 	if (cmd.status != CMD_STATUS_SUCCESS) | ||||
| 		wl12xx_error("error in read command result: %d", cmd.status); | ||||
| 
 | ||||
| 	mem_answer = (struct cmd_read_write_memory *) cmd.parameters; | ||||
| 	memcpy(answer, mem_answer->value, len); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_cmd_template_set(struct wl12xx *wl, u16 cmd_id, | ||||
| 			    void *buf, size_t buf_len) | ||||
| { | ||||
| 	struct wl12xx_cmd_packet_template template; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_CMD, "cmd template %d", cmd_id); | ||||
| 
 | ||||
| 	memset(&template, 0, sizeof(template)); | ||||
| 
 | ||||
| 	WARN_ON(buf_len > WL12XX_MAX_TEMPLATE_SIZE); | ||||
| 	buf_len = min_t(size_t, buf_len, WL12XX_MAX_TEMPLATE_SIZE); | ||||
| 	template.size = cpu_to_le16(buf_len); | ||||
| 
 | ||||
| 	if (buf) | ||||
| 		memcpy(template.template, buf, buf_len); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_send(wl, cmd_id, &template, | ||||
| 			      sizeof(template.size) + buf_len); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("cmd set_template failed: %d", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										265
									
								
								drivers/net/wireless/wl12xx/cmd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								drivers/net/wireless/wl12xx/cmd.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,265 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_CMD_H__ | ||||
| #define __WL12XX_CMD_H__ | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| 
 | ||||
| int wl12xx_cmd_send(struct wl12xx *wl, u16 type, void *buf, size_t buf_len); | ||||
| int wl12xx_cmd_test(struct wl12xx *wl, void *buf, size_t buf_len, u8 answer); | ||||
| int wl12xx_cmd_interrogate(struct wl12xx *wl, u16 ie_id, u16 ie_len, | ||||
| 			   void *answer); | ||||
| int wl12xx_cmd_configure(struct wl12xx *wl, void *ie, int ie_len); | ||||
| int wl12xx_cmd_vbm(struct wl12xx *wl, u8 identity, | ||||
| 		   void *bitmap, u16 bitmap_len, u8 bitmap_control); | ||||
| int wl12xx_cmd_data_path(struct wl12xx *wl, u8 channel, u8 enable); | ||||
| int wl12xx_cmd_join(struct wl12xx *wl, u8 bss_type, u8 dtim_interval, | ||||
| 		    u16 beacon_interval, u8 wait); | ||||
| int wl12xx_cmd_ps_mode(struct wl12xx *wl, u8 ps_mode); | ||||
| int wl12xx_cmd_read_memory(struct wl12xx *wl, u32 addr, u32 len, void *answer); | ||||
| int wl12xx_cmd_template_set(struct wl12xx *wl, u16 cmd_id, | ||||
| 			    void *buf, size_t buf_len); | ||||
| 
 | ||||
| /* unit ms */ | ||||
| #define WL12XX_COMMAND_TIMEOUT 2000 | ||||
| 
 | ||||
| #define WL12XX_MAX_TEMPLATE_SIZE 300 | ||||
| 
 | ||||
| struct wl12xx_cmd_packet_template { | ||||
| 	__le16 size; | ||||
| 	u8 template[WL12XX_MAX_TEMPLATE_SIZE]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| enum wl12xx_commands { | ||||
| 	CMD_RESET           = 0, | ||||
| 	CMD_INTERROGATE     = 1,    /*use this to read information elements*/ | ||||
| 	CMD_CONFIGURE       = 2,    /*use this to write information elements*/ | ||||
| 	CMD_ENABLE_RX       = 3, | ||||
| 	CMD_ENABLE_TX       = 4, | ||||
| 	CMD_DISABLE_RX      = 5, | ||||
| 	CMD_DISABLE_TX      = 6, | ||||
| 	CMD_SCAN            = 8, | ||||
| 	CMD_STOP_SCAN       = 9, | ||||
| 	CMD_VBM             = 10, | ||||
| 	CMD_START_JOIN      = 11, | ||||
| 	CMD_SET_KEYS        = 12, | ||||
| 	CMD_READ_MEMORY     = 13, | ||||
| 	CMD_WRITE_MEMORY    = 14, | ||||
| 	CMD_BEACON          = 19, | ||||
| 	CMD_PROBE_RESP      = 20, | ||||
| 	CMD_NULL_DATA       = 21, | ||||
| 	CMD_PROBE_REQ       = 22, | ||||
| 	CMD_TEST            = 23, | ||||
| 	CMD_RADIO_CALIBRATE     = 25,   /* OBSOLETE */ | ||||
| 	CMD_ENABLE_RX_PATH      = 27,   /* OBSOLETE */ | ||||
| 	CMD_NOISE_HIST      = 28, | ||||
| 	CMD_RX_RESET        = 29, | ||||
| 	CMD_PS_POLL         = 30, | ||||
| 	CMD_QOS_NULL_DATA   = 31, | ||||
| 	CMD_LNA_CONTROL     = 32, | ||||
| 	CMD_SET_BCN_MODE    = 33, | ||||
| 	CMD_MEASUREMENT      = 34, | ||||
| 	CMD_STOP_MEASUREMENT = 35, | ||||
| 	CMD_DISCONNECT       = 36, | ||||
| 	CMD_SET_PS_MODE      = 37, | ||||
| 	CMD_CHANNEL_SWITCH   = 38, | ||||
| 	CMD_STOP_CHANNEL_SWICTH = 39, | ||||
| 	CMD_AP_DISCOVERY     = 40, | ||||
| 	CMD_STOP_AP_DISCOVERY = 41, | ||||
| 	CMD_SPS_SCAN = 42, | ||||
| 	CMD_STOP_SPS_SCAN = 43, | ||||
| 	CMD_HEALTH_CHECK     = 45, | ||||
| 	CMD_DEBUG            = 46, | ||||
| 	CMD_TRIGGER_SCAN_TO  = 47, | ||||
| 
 | ||||
| 	NUM_COMMANDS, | ||||
| 	MAX_COMMAND_ID = 0xFFFF, | ||||
| }; | ||||
| 
 | ||||
| #define MAX_CMD_PARAMS 572 | ||||
| 
 | ||||
| struct  wl12xx_command { | ||||
| 	u16 id; | ||||
| 	u16 status; | ||||
| 	u8  parameters[MAX_CMD_PARAMS]; | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	CMD_MAILBOX_IDLE              		=  0, | ||||
| 	CMD_STATUS_SUCCESS            		=  1, | ||||
| 	CMD_STATUS_UNKNOWN_CMD        		=  2, | ||||
| 	CMD_STATUS_UNKNOWN_IE         		=  3, | ||||
| 	CMD_STATUS_REJECT_MEAS_SG_ACTIVE 	= 11, | ||||
| 	CMD_STATUS_RX_BUSY            		= 13, | ||||
| 	CMD_STATUS_INVALID_PARAM      		= 14, | ||||
| 	CMD_STATUS_TEMPLATE_TOO_LARGE 		= 15, | ||||
| 	CMD_STATUS_OUT_OF_MEMORY      		= 16, | ||||
| 	CMD_STATUS_STA_TABLE_FULL     		= 17, | ||||
| 	CMD_STATUS_RADIO_ERROR        		= 18, | ||||
| 	CMD_STATUS_WRONG_NESTING      		= 19, | ||||
| 	CMD_STATUS_TIMEOUT            		= 21, /* Driver internal use.*/ | ||||
| 	CMD_STATUS_FW_RESET           		= 22, /* Driver internal use.*/ | ||||
| 	MAX_COMMAND_STATUS            		= 0xff | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * CMD_READ_MEMORY | ||||
|  * | ||||
|  * The host issues this command to read the WiLink device memory/registers. | ||||
|  * | ||||
|  * Note: The Base Band address has special handling (16 bits registers and | ||||
|  * addresses). For more information, see the hardware specification. | ||||
|  */ | ||||
| /*
 | ||||
|  * CMD_WRITE_MEMORY | ||||
|  * | ||||
|  * The host issues this command to write the WiLink device memory/registers. | ||||
|  * | ||||
|  * The Base Band address has special handling (16 bits registers and | ||||
|  * addresses). For more information, see the hardware specification. | ||||
|  */ | ||||
| #define MAX_READ_SIZE 256 | ||||
| 
 | ||||
| struct cmd_read_write_memory { | ||||
| 	/* The address of the memory to read from or write to.*/ | ||||
| 	u32 addr; | ||||
| 
 | ||||
| 	/* The amount of data in bytes to read from or write to the WiLink
 | ||||
| 	 * device.*/ | ||||
| 	u32 size; | ||||
| 
 | ||||
| 	/* The actual value read from or written to the Wilink. The source
 | ||||
| 	   of this field is the Host in WRITE command or the Wilink in READ | ||||
| 	   command. */ | ||||
| 	u8 value[MAX_READ_SIZE]; | ||||
| }; | ||||
| 
 | ||||
| #define CMDMBOX_HEADER_LEN 4 | ||||
| #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 | ||||
| 
 | ||||
| 
 | ||||
| struct basic_scan_parameters { | ||||
| 	u32 rx_config_options; | ||||
| 	u32 rx_filter_options; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Scan options: | ||||
| 	 * bit 0: When this bit is set, passive scan. | ||||
| 	 * bit 1: Band, when this bit is set we scan | ||||
| 	 * in the 5Ghz band. | ||||
| 	 * bit 2: voice mode, 0 for normal scan. | ||||
| 	 * bit 3: scan priority, 1 for high priority. | ||||
| 	 */ | ||||
| 	u16 scan_options; | ||||
| 
 | ||||
| 	/* Number of channels to scan */ | ||||
| 	u8 num_channels; | ||||
| 
 | ||||
| 	/* Number opf probe requests to send, per channel */ | ||||
| 	u8 num_probe_requests; | ||||
| 
 | ||||
| 	/* Rate and modulation for probe requests */ | ||||
| 	u16 tx_rate; | ||||
| 
 | ||||
| 	u8 tid_trigger; | ||||
| 	u8 ssid_len; | ||||
| 	u32 ssid[8]; | ||||
| 
 | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct basic_scan_channel_parameters { | ||||
| 	u32 min_duration; /* in TU */ | ||||
| 	u32 max_duration; /* in TU */ | ||||
| 	u32 bssid_lsb; | ||||
| 	u16 bssid_msb; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * bits 0-3: Early termination count. | ||||
| 	 * bits 4-5: Early termination condition. | ||||
| 	 */ | ||||
| 	u8 early_termination; | ||||
| 
 | ||||
| 	u8 tx_power_att; | ||||
| 	u8 channel; | ||||
| 	u8 pad[3]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /* SCAN parameters */ | ||||
| #define SCAN_MAX_NUM_OF_CHANNELS 16 | ||||
| 
 | ||||
| struct cmd_scan { | ||||
| 	struct basic_scan_parameters params; | ||||
| 	struct basic_scan_channel_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| enum { | ||||
| 	BSS_TYPE_IBSS = 0, | ||||
| 	BSS_TYPE_STA_BSS = 2, | ||||
| 	BSS_TYPE_AP_BSS = 3, | ||||
| 	MAX_BSS_TYPE = 0xFF | ||||
| }; | ||||
| 
 | ||||
| #define JOIN_CMD_CTRL_TX_FLUSH             0x80 /* Firmware flushes all Tx */ | ||||
| #define JOIN_CMD_CTRL_EARLY_WAKEUP_ENABLE  0x01 /* Early wakeup time */ | ||||
| 
 | ||||
| 
 | ||||
| struct cmd_join { | ||||
| 	u32 bssid_lsb; | ||||
| 	u16 bssid_msb; | ||||
| 	u16 beacon_interval; /* in TBTTs */ | ||||
| 	u32 rx_config_options; | ||||
| 	u32 rx_filter_options; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The target uses this field to determine the rate at | ||||
| 	 * which to transmit control frame responses (such as | ||||
| 	 * ACK or CTS frames). | ||||
| 	 */ | ||||
| 	u16 basic_rate_set; | ||||
| 	u8 dtim_interval; | ||||
| 	u8 tx_ctrl_frame_rate; /* OBSOLETE */ | ||||
| 	u8 tx_ctrl_frame_mod;  /* OBSOLETE */ | ||||
| 	/*
 | ||||
| 	 * bits 0-2: This bitwise field specifies the type | ||||
| 	 * of BSS to start or join (BSS_TYPE_*). | ||||
| 	 * bit 4: Band - The radio band in which to join | ||||
| 	 * or start. | ||||
| 	 *  0 - 2.4GHz band | ||||
| 	 *  1 - 5GHz band | ||||
| 	 * bits 3, 5-7: Reserved | ||||
| 	 */ | ||||
| 	u8 bss_type; | ||||
| 	u8 channel; | ||||
| 	u8 ssid_len; | ||||
| 	u8 ssid[IW_ESSID_MAX_SIZE]; | ||||
| 	u8 ctrl; /* JOIN_CMD_CTRL_* */ | ||||
| 	u8 tx_mgt_frame_rate; /* OBSOLETE */ | ||||
| 	u8 tx_mgt_frame_mod;  /* OBSOLETE */ | ||||
| 	u8 reserved; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __WL12XX_CMD_H__ */ | ||||
							
								
								
									
										508
									
								
								drivers/net/wireless/wl12xx/debugfs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										508
									
								
								drivers/net/wireless/wl12xx/debugfs.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,508 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "debugfs.h" | ||||
| 
 | ||||
| #include <linux/skbuff.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "acx.h" | ||||
| 
 | ||||
| /* ms */ | ||||
| #define WL12XX_DEBUGFS_STATS_LIFETIME 1000 | ||||
| 
 | ||||
| /* debugfs macros idea from mac80211 */ | ||||
| 
 | ||||
| #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...)		\ | ||||
| static ssize_t name## _read(struct file *file, char __user *userbuf,	\ | ||||
| 			    size_t count, loff_t *ppos)			\ | ||||
| {									\ | ||||
| 	struct wl12xx *wl = file->private_data;				\ | ||||
| 	char buf[buflen];						\ | ||||
| 	int res;							\ | ||||
| 									\ | ||||
| 	res = scnprintf(buf, buflen, fmt "\n", ##value);		\ | ||||
| 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);	\ | ||||
| }									\ | ||||
| 									\ | ||||
| static const struct file_operations name## _ops = {			\ | ||||
| 	.read = name## _read,						\ | ||||
| 	.open = wl12xx_open_file_generic,				\ | ||||
| }; | ||||
| 
 | ||||
| #define DEBUGFS_ADD(name, parent)					\ | ||||
| 	wl->debugfs.name = debugfs_create_file(#name, 0400, parent,	\ | ||||
| 					       wl, &name## _ops);	\ | ||||
| 	if (IS_ERR(wl->debugfs.name)) {					\ | ||||
| 		ret = PTR_ERR(wl->debugfs.name);			\ | ||||
| 		wl->debugfs.name = NULL;				\ | ||||
| 		goto out;						\ | ||||
| 	} | ||||
| 
 | ||||
| #define DEBUGFS_DEL(name)						\ | ||||
| 	do {								\ | ||||
| 		debugfs_remove(wl->debugfs.name);			\ | ||||
| 		wl->debugfs.name = NULL;				\ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define DEBUGFS_FWSTATS_FILE(sub, name, buflen, fmt)			\ | ||||
| static ssize_t sub## _ ##name## _read(struct file *file,		\ | ||||
| 				      char __user *userbuf,		\ | ||||
| 				      size_t count, loff_t *ppos)	\ | ||||
| {									\ | ||||
| 	struct wl12xx *wl = file->private_data;				\ | ||||
| 	char buf[buflen];						\ | ||||
| 	int res;							\ | ||||
| 									\ | ||||
| 	wl12xx_debugfs_update_stats(wl);				\ | ||||
| 									\ | ||||
| 	res = scnprintf(buf, buflen, fmt "\n",				\ | ||||
| 			wl->stats.fw_stats->sub.name);			\ | ||||
| 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);	\ | ||||
| }									\ | ||||
| 									\ | ||||
| static const struct file_operations sub## _ ##name## _ops = {		\ | ||||
| 	.read = sub## _ ##name## _read,					\ | ||||
| 	.open = wl12xx_open_file_generic,				\ | ||||
| }; | ||||
| 
 | ||||
| #define DEBUGFS_FWSTATS_ADD(sub, name)				\ | ||||
| 	DEBUGFS_ADD(sub## _ ##name, wl->debugfs.fw_statistics) | ||||
| 
 | ||||
| #define DEBUGFS_FWSTATS_DEL(sub, name)				\ | ||||
| 	DEBUGFS_DEL(sub## _ ##name) | ||||
| 
 | ||||
| static void wl12xx_debugfs_update_stats(struct wl12xx *wl) | ||||
| { | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 
 | ||||
| 	if (wl->state == WL12XX_STATE_ON && | ||||
| 	    time_after(jiffies, wl->stats.fw_stats_update + | ||||
| 		       msecs_to_jiffies(WL12XX_DEBUGFS_STATS_LIFETIME))) { | ||||
| 		wl12xx_acx_statistics(wl, wl->stats.fw_stats); | ||||
| 		wl->stats.fw_stats_update = jiffies; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_open_file_generic(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	file->private_data = inode->i_private; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(rx, out_of_mem, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, hw_stuck, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, dropped, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, fcs_err, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, path_reset, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rx, reset_counter, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(dma, rx_requested, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(dma, rx_errors, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(dma, tx_requested, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(dma, tx_errors, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, fiqs, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, rx_headers, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, rx_rdys, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, irqs, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, tx_procs, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, decrypt_done, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, dma0_done, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, dma1_done, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, commands, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, rx_procs, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, pci_pm, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, wakeups, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(isr, low_rssi, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(wep, addr_key_count, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(wep, default_key_count, 20, "%u"); | ||||
| /* skipping wep.reserved */ | ||||
| DEBUGFS_FWSTATS_FILE(wep, key_not_found, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(wep, packets, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(wep, interrupt, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(pwr, ps_enter, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, elp_enter, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, power_save_off, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, enable_ps, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, disable_ps, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, 20, "%u"); | ||||
| /* skipping cont_miss_bcns_spread for now */ | ||||
| DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(mic, rx_pkts, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(mic, calc_failure, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(event, heart_beat, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, calibration, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, rx_mismatch, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, rx_pool, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, oom_late, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(event, tx_stuck, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, | ||||
| 		     20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, 20, "%u"); | ||||
| DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, 20, "%u"); | ||||
| 
 | ||||
| DEBUGFS_READONLY_FILE(retry_count, 20, "%u", wl->stats.retry_count); | ||||
| DEBUGFS_READONLY_FILE(excessive_retries, 20, "%u", | ||||
| 		      wl->stats.excessive_retries); | ||||
| 
 | ||||
| static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, | ||||
| 				 size_t count, loff_t *ppos) | ||||
| { | ||||
| 	struct wl12xx *wl = file->private_data; | ||||
| 	u32 queue_len; | ||||
| 	char buf[20]; | ||||
| 	int res; | ||||
| 
 | ||||
| 	queue_len = skb_queue_len(&wl->tx_queue); | ||||
| 
 | ||||
| 	res = scnprintf(buf, sizeof(buf), "%u\n", queue_len); | ||||
| 	return simple_read_from_buffer(userbuf, count, ppos, buf, res); | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations tx_queue_len_ops = { | ||||
| 	.read = tx_queue_len_read, | ||||
| 	.open = wl12xx_open_file_generic, | ||||
| }; | ||||
| 
 | ||||
| static void wl12xx_debugfs_delete_files(struct wl12xx *wl) | ||||
| { | ||||
| 	DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, out_of_mem); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, hdr_overflow); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, hw_stuck); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, dropped); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, fcs_err); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, xfr_hint_trig); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, path_reset); | ||||
| 	DEBUGFS_FWSTATS_DEL(rx, reset_counter); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(dma, rx_requested); | ||||
| 	DEBUGFS_FWSTATS_DEL(dma, rx_errors); | ||||
| 	DEBUGFS_FWSTATS_DEL(dma, tx_requested); | ||||
| 	DEBUGFS_FWSTATS_DEL(dma, tx_errors); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, cmd_cmplt); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, fiqs); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, rx_headers); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, rx_mem_overflow); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, rx_rdys); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, irqs); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, tx_procs); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, decrypt_done); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, dma0_done); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, dma1_done); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, tx_exch_complete); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, commands); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, rx_procs); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, hw_pm_mode_changes); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, host_acknowledges); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, pci_pm); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, wakeups); | ||||
| 	DEBUGFS_FWSTATS_DEL(isr, low_rssi); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(wep, addr_key_count); | ||||
| 	DEBUGFS_FWSTATS_DEL(wep, default_key_count); | ||||
| 	/* skipping wep.reserved */ | ||||
| 	DEBUGFS_FWSTATS_DEL(wep, key_not_found); | ||||
| 	DEBUGFS_FWSTATS_DEL(wep, decrypt_fail); | ||||
| 	DEBUGFS_FWSTATS_DEL(wep, packets); | ||||
| 	DEBUGFS_FWSTATS_DEL(wep, interrupt); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, ps_enter); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, elp_enter); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, missing_bcns); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, wake_on_host); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, wake_on_timer_exp); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, tx_with_ps); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, tx_without_ps); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, rcvd_beacons); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, power_save_off); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, enable_ps); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, disable_ps); | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, fix_tsf_ps); | ||||
| 	/* skipping cont_miss_bcns_spread for now */ | ||||
| 	DEBUGFS_FWSTATS_DEL(pwr, rcvd_awake_beacons); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(mic, rx_pkts); | ||||
| 	DEBUGFS_FWSTATS_DEL(mic, calc_failure); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(aes, encrypt_fail); | ||||
| 	DEBUGFS_FWSTATS_DEL(aes, decrypt_fail); | ||||
| 	DEBUGFS_FWSTATS_DEL(aes, encrypt_packets); | ||||
| 	DEBUGFS_FWSTATS_DEL(aes, decrypt_packets); | ||||
| 	DEBUGFS_FWSTATS_DEL(aes, encrypt_interrupt); | ||||
| 	DEBUGFS_FWSTATS_DEL(aes, decrypt_interrupt); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(event, heart_beat); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, calibration); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, rx_mismatch); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, rx_mem_empty); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, rx_pool); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, oom_late); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, phy_transmit_error); | ||||
| 	DEBUGFS_FWSTATS_DEL(event, tx_stuck); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, pspoll_timeouts); | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, upsd_timeouts); | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, upsd_max_sptime); | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, upsd_max_apturn); | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, pspoll_max_apturn); | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, pspoll_utilization); | ||||
| 	DEBUGFS_FWSTATS_DEL(ps, upsd_utilization); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_DEL(rxpipe, rx_prep_beacon_drop); | ||||
| 	DEBUGFS_FWSTATS_DEL(rxpipe, descr_host_int_trig_rx_data); | ||||
| 	DEBUGFS_FWSTATS_DEL(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); | ||||
| 	DEBUGFS_FWSTATS_DEL(rxpipe, missed_beacon_host_int_trig_rx_data); | ||||
| 	DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data); | ||||
| 
 | ||||
| 	DEBUGFS_DEL(tx_queue_len); | ||||
| 	DEBUGFS_DEL(retry_count); | ||||
| 	DEBUGFS_DEL(excessive_retries); | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_debugfs_add_files(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, out_of_mem); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, hdr_overflow); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, hw_stuck); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, dropped); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, fcs_err); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, path_reset); | ||||
| 	DEBUGFS_FWSTATS_ADD(rx, reset_counter); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(dma, rx_requested); | ||||
| 	DEBUGFS_FWSTATS_ADD(dma, rx_errors); | ||||
| 	DEBUGFS_FWSTATS_ADD(dma, tx_requested); | ||||
| 	DEBUGFS_FWSTATS_ADD(dma, tx_errors); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, fiqs); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, rx_headers); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, rx_rdys); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, irqs); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, tx_procs); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, decrypt_done); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, dma0_done); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, dma1_done); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, commands); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, rx_procs); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, host_acknowledges); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, pci_pm); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, wakeups); | ||||
| 	DEBUGFS_FWSTATS_ADD(isr, low_rssi); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(wep, addr_key_count); | ||||
| 	DEBUGFS_FWSTATS_ADD(wep, default_key_count); | ||||
| 	/* skipping wep.reserved */ | ||||
| 	DEBUGFS_FWSTATS_ADD(wep, key_not_found); | ||||
| 	DEBUGFS_FWSTATS_ADD(wep, decrypt_fail); | ||||
| 	DEBUGFS_FWSTATS_ADD(wep, packets); | ||||
| 	DEBUGFS_FWSTATS_ADD(wep, interrupt); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, ps_enter); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, elp_enter); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, missing_bcns); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, wake_on_host); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, power_save_off); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, enable_ps); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, disable_ps); | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps); | ||||
| 	/* skipping cont_miss_bcns_spread for now */ | ||||
| 	DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(mic, rx_pkts); | ||||
| 	DEBUGFS_FWSTATS_ADD(mic, calc_failure); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(aes, encrypt_fail); | ||||
| 	DEBUGFS_FWSTATS_ADD(aes, decrypt_fail); | ||||
| 	DEBUGFS_FWSTATS_ADD(aes, encrypt_packets); | ||||
| 	DEBUGFS_FWSTATS_ADD(aes, decrypt_packets); | ||||
| 	DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt); | ||||
| 	DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(event, heart_beat); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, calibration); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, rx_mismatch); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, rx_pool); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, oom_late); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, phy_transmit_error); | ||||
| 	DEBUGFS_FWSTATS_ADD(event, tx_stuck); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts); | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts); | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime); | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn); | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn); | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization); | ||||
| 	DEBUGFS_FWSTATS_ADD(ps, upsd_utilization); | ||||
| 
 | ||||
| 	DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop); | ||||
| 	DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data); | ||||
| 	DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); | ||||
| 	DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); | ||||
| 	DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); | ||||
| 
 | ||||
| 	DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir); | ||||
| 	DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); | ||||
| 	DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); | ||||
| 
 | ||||
| out: | ||||
| 	if (ret < 0) | ||||
| 		wl12xx_debugfs_delete_files(wl); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void wl12xx_debugfs_reset(struct wl12xx *wl) | ||||
| { | ||||
| 	memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); | ||||
| 	wl->stats.retry_count = 0; | ||||
| 	wl->stats.excessive_retries = 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_debugfs_init(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl->debugfs.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||||
| 
 | ||||
| 	if (IS_ERR(wl->debugfs.rootdir)) { | ||||
| 		ret = PTR_ERR(wl->debugfs.rootdir); | ||||
| 		wl->debugfs.rootdir = NULL; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	wl->debugfs.fw_statistics = debugfs_create_dir("fw-statistics", | ||||
| 						       wl->debugfs.rootdir); | ||||
| 
 | ||||
| 	if (IS_ERR(wl->debugfs.fw_statistics)) { | ||||
| 		ret = PTR_ERR(wl->debugfs.fw_statistics); | ||||
| 		wl->debugfs.fw_statistics = NULL; | ||||
| 		goto err_root; | ||||
| 	} | ||||
| 
 | ||||
| 	wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats), | ||||
| 				      GFP_KERNEL); | ||||
| 
 | ||||
| 	if (!wl->stats.fw_stats) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto err_fw; | ||||
| 	} | ||||
| 
 | ||||
| 	wl->stats.fw_stats_update = jiffies; | ||||
| 
 | ||||
| 	ret = wl12xx_debugfs_add_files(wl); | ||||
| 
 | ||||
| 	if (ret < 0) | ||||
| 		goto err_file; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_file: | ||||
| 	kfree(wl->stats.fw_stats); | ||||
| 	wl->stats.fw_stats = NULL; | ||||
| 
 | ||||
| err_fw: | ||||
| 	debugfs_remove(wl->debugfs.fw_statistics); | ||||
| 	wl->debugfs.fw_statistics = NULL; | ||||
| 
 | ||||
| err_root: | ||||
| 	debugfs_remove(wl->debugfs.rootdir); | ||||
| 	wl->debugfs.rootdir = NULL; | ||||
| 
 | ||||
| err: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void wl12xx_debugfs_exit(struct wl12xx *wl) | ||||
| { | ||||
| 	wl12xx_debugfs_delete_files(wl); | ||||
| 
 | ||||
| 	kfree(wl->stats.fw_stats); | ||||
| 	wl->stats.fw_stats = NULL; | ||||
| 
 | ||||
| 	debugfs_remove(wl->debugfs.fw_statistics); | ||||
| 	wl->debugfs.fw_statistics = NULL; | ||||
| 
 | ||||
| 	debugfs_remove(wl->debugfs.rootdir); | ||||
| 	wl->debugfs.rootdir = NULL; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										33
									
								
								drivers/net/wireless/wl12xx/debugfs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								drivers/net/wireless/wl12xx/debugfs.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef WL12XX_DEBUGFS_H | ||||
| #define WL12XX_DEBUGFS_H | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| 
 | ||||
| int wl12xx_debugfs_init(struct wl12xx *wl); | ||||
| void wl12xx_debugfs_exit(struct wl12xx *wl); | ||||
| void wl12xx_debugfs_reset(struct wl12xx *wl); | ||||
| 
 | ||||
| #endif /* WL12XX_DEBUGFS_H */ | ||||
							
								
								
									
										127
									
								
								drivers/net/wireless/wl12xx/event.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								drivers/net/wireless/wl12xx/event.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "event.h" | ||||
| #include "ps.h" | ||||
| 
 | ||||
| static int wl12xx_event_scan_complete(struct wl12xx *wl, | ||||
| 				      struct event_mailbox *mbox) | ||||
| { | ||||
| 	wl12xx_debug(DEBUG_EVENT, "status: 0x%x, channels: %d", | ||||
| 		     mbox->scheduled_scan_status, | ||||
| 		     mbox->scheduled_scan_channels); | ||||
| 
 | ||||
| 	if (wl->scanning) { | ||||
| 		mutex_unlock(&wl->mutex); | ||||
| 		ieee80211_scan_completed(wl->hw, false); | ||||
| 		mutex_lock(&wl->mutex); | ||||
| 		wl->scanning = false; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_event_mbox_dump(struct event_mailbox *mbox) | ||||
| { | ||||
| 	wl12xx_debug(DEBUG_EVENT, "MBOX DUMP:"); | ||||
| 	wl12xx_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); | ||||
| 	wl12xx_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_event_process(struct wl12xx *wl, struct event_mailbox *mbox) | ||||
| { | ||||
| 	int ret; | ||||
| 	u32 vector; | ||||
| 
 | ||||
| 	wl12xx_event_mbox_dump(mbox); | ||||
| 
 | ||||
| 	vector = mbox->events_vector & ~(mbox->events_mask); | ||||
| 	wl12xx_debug(DEBUG_EVENT, "vector: 0x%x", vector); | ||||
| 
 | ||||
| 	if (vector & SCAN_COMPLETE_EVENT_ID) { | ||||
| 		ret = wl12xx_event_scan_complete(wl, mbox); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (vector & BSS_LOSE_EVENT_ID) { | ||||
| 		wl12xx_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); | ||||
| 
 | ||||
| 		if (wl->psm_requested && wl->psm) { | ||||
| 			ret = wl12xx_ps_set_mode(wl, STATION_ACTIVE_MODE); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_event_unmask(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_event_mbox_mask(wl, ~(wl->event_mask)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void wl12xx_event_mbox_config(struct wl12xx *wl) | ||||
| { | ||||
| 	wl->mbox_ptr[0] = wl12xx_reg_read32(wl, REG_EVENT_MAILBOX_PTR); | ||||
| 	wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", | ||||
| 		     wl->mbox_ptr[0], wl->mbox_ptr[1]); | ||||
| } | ||||
| 
 | ||||
| int wl12xx_event_handle(struct wl12xx *wl, u8 mbox_num) | ||||
| { | ||||
| 	struct event_mailbox mbox; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); | ||||
| 
 | ||||
| 	if (mbox_num > 1) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* first we read the mbox descriptor */ | ||||
| 	wl12xx_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, | ||||
| 			    sizeof(struct event_mailbox)); | ||||
| 
 | ||||
| 	/* process the descriptor */ | ||||
| 	ret = wl12xx_event_process(wl, &mbox); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* then we let the firmware know it can go on...*/ | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										121
									
								
								drivers/net/wireless/wl12xx/event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								drivers/net/wireless/wl12xx/event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_EVENT_H__ | ||||
| #define __WL12XX_EVENT_H__ | ||||
| 
 | ||||
| /*
 | ||||
|  * Mbox events | ||||
|  * | ||||
|  * The event mechanism is based on a pair of event buffers (buffers A and | ||||
|  * B) at fixed locations in the target's memory. The host processes one | ||||
|  * buffer while the other buffer continues to collect events. If the host | ||||
|  * is not processing events, an interrupt is issued to signal that a buffer | ||||
|  * is ready. Once the host is done with processing events from one buffer, | ||||
|  * it signals the target (with an ACK interrupt) that the event buffer is | ||||
|  * free. | ||||
|  */ | ||||
| 
 | ||||
| enum { | ||||
| 	RESERVED1_EVENT_ID                       = BIT(0), | ||||
| 	RESERVED2_EVENT_ID                       = BIT(1), | ||||
| 	MEASUREMENT_START_EVENT_ID               = BIT(2), | ||||
| 	SCAN_COMPLETE_EVENT_ID                   = BIT(3), | ||||
| 	CALIBRATION_COMPLETE_EVENT_ID            = BIT(4), | ||||
| 	ROAMING_TRIGGER_LOW_RSSI_EVENT_ID        = BIT(5), | ||||
| 	PS_REPORT_EVENT_ID                       = BIT(6), | ||||
| 	SYNCHRONIZATION_TIMEOUT_EVENT_ID         = BIT(7), | ||||
| 	HEALTH_REPORT_EVENT_ID                   = BIT(8), | ||||
| 	ACI_DETECTION_EVENT_ID                   = BIT(9), | ||||
| 	DEBUG_REPORT_EVENT_ID                    = BIT(10), | ||||
| 	MAC_STATUS_EVENT_ID                      = BIT(11), | ||||
| 	DISCONNECT_EVENT_COMPLETE_ID             = BIT(12), | ||||
| 	JOIN_EVENT_COMPLETE_ID                   = BIT(13), | ||||
| 	CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(14), | ||||
| 	BSS_LOSE_EVENT_ID                        = BIT(15), | ||||
| 	ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID    = BIT(16), | ||||
| 	MEASUREMENT_COMPLETE_EVENT_ID            = BIT(17), | ||||
| 	AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(18), | ||||
| 	SCHEDULED_SCAN_COMPLETE_EVENT_ID         = BIT(19), | ||||
| 	PSPOLL_DELIVERY_FAILURE_EVENT_ID 	 = BIT(20), | ||||
| 	RESET_BSS_EVENT_ID                       = BIT(21), | ||||
| 	REGAINED_BSS_EVENT_ID                    = BIT(22), | ||||
| 	ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID   = BIT(23), | ||||
| 	ROAMING_TRIGGER_LOW_SNR_EVENT_ID         = BIT(24), | ||||
| 	ROAMING_TRIGGER_REGAINED_SNR_EVENT_ID    = BIT(25), | ||||
| 
 | ||||
| 	DBG_EVENT_ID                             = BIT(26), | ||||
| 	BT_PTA_SENSE_EVENT_ID                    = BIT(27), | ||||
| 	BT_PTA_PREDICTION_EVENT_ID               = BIT(28), | ||||
| 	BT_PTA_AVALANCHE_EVENT_ID                = BIT(29), | ||||
| 
 | ||||
| 	PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(30), | ||||
| 
 | ||||
| 	EVENT_MBOX_ALL_EVENT_ID                  = 0x7fffffff, | ||||
| }; | ||||
| 
 | ||||
| struct event_debug_report { | ||||
| 	u8 debug_event_id; | ||||
| 	u8 num_params; | ||||
| 	u16 pad; | ||||
| 	u32 report_1; | ||||
| 	u32 report_2; | ||||
| 	u32 report_3; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct event_mailbox { | ||||
| 	u32 events_vector; | ||||
| 	u32 events_mask; | ||||
| 	u32 reserved_1; | ||||
| 	u32 reserved_2; | ||||
| 
 | ||||
| 	char average_rssi_level; | ||||
| 	u8 ps_status; | ||||
| 	u8 channel_switch_status; | ||||
| 	u8 scheduled_scan_status; | ||||
| 
 | ||||
| 	/* Channels scanned by the scheduled scan */ | ||||
| 	u16 scheduled_scan_channels; | ||||
| 
 | ||||
| 	/* If bit 0 is set -> target's fatal error */ | ||||
| 	u16 health_report; | ||||
| 	u16 bad_fft_counter; | ||||
| 	u8 bt_pta_sense_info; | ||||
| 	u8 bt_pta_protective_info; | ||||
| 	u32 reserved; | ||||
| 	u32 debug_report[2]; | ||||
| 
 | ||||
| 	/* Number of FCS errors since last event */ | ||||
| 	u32 fcs_err_counter; | ||||
| 
 | ||||
| 	struct event_debug_report report; | ||||
| 	u8 average_snr_level; | ||||
| 	u8 padding[19]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| int wl12xx_event_unmask(struct wl12xx *wl); | ||||
| void wl12xx_event_mbox_config(struct wl12xx *wl); | ||||
| int wl12xx_event_handle(struct wl12xx *wl, u8 mbox); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										200
									
								
								drivers/net/wireless/wl12xx/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								drivers/net/wireless/wl12xx/init.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include "init.h" | ||||
| #include "wl12xx_80211.h" | ||||
| #include "acx.h" | ||||
| #include "cmd.h" | ||||
| 
 | ||||
| int wl12xx_hw_init_hwenc_config(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_feature_cfg(wl); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("couldn't set feature config"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = wl12xx_acx_default_key(wl, wl->default_key); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_warning("couldn't set default key"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_templates_config(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 	u8 partial_vbm[PARTIAL_VBM_MAX]; | ||||
| 
 | ||||
| 	/* send empty templates for fw memory reservation */ | ||||
| 	ret = wl12xx_cmd_template_set(wl, CMD_PROBE_REQ, NULL, | ||||
| 				      sizeof(struct wl12xx_probe_req_template)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_template_set(wl, CMD_NULL_DATA, NULL, | ||||
| 				      sizeof(struct wl12xx_null_data_template)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_template_set(wl, CMD_PS_POLL, NULL, | ||||
| 				      sizeof(struct wl12xx_ps_poll_template)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL, | ||||
| 				      sizeof | ||||
| 				      (struct wl12xx_qos_null_data_template)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_template_set(wl, CMD_PROBE_RESP, NULL, | ||||
| 				      sizeof | ||||
| 				      (struct wl12xx_probe_resp_template)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_template_set(wl, CMD_BEACON, NULL, | ||||
| 				      sizeof | ||||
| 				      (struct wl12xx_beacon_template)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* tim templates, first reserve space then allocate an empty one */ | ||||
| 	memset(partial_vbm, 0, PARTIAL_VBM_MAX); | ||||
| 	ret = wl12xx_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_rx_config(struct wl12xx *wl, u32 config, u32 filter) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_rx_config(wl, config, filter); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_phy_config(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_pd_threshold(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_slot(wl, DEFAULT_SLOT_TIME); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_group_address_tbl(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_service_period_timeout(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_beacon_filter(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_beacon_filter_opt(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_beacon_filter_table(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_pta(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_sg_enable(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_sg_cfg(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_energy_detection(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_cca_threshold(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_beacon_broadcast(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_acx_bcn_dtim_options(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_hw_init_power_auth(struct wl12xx *wl) | ||||
| { | ||||
| 	return wl12xx_acx_sleep_auth(wl, WL12XX_PSM_CAM); | ||||
| } | ||||
							
								
								
									
										40
									
								
								drivers/net/wireless/wl12xx/init.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								drivers/net/wireless/wl12xx/init.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_INIT_H__ | ||||
| #define __WL12XX_INIT_H__ | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| 
 | ||||
| int wl12xx_hw_init_hwenc_config(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_templates_config(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_mem_config(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_rx_config(struct wl12xx *wl, u32 config, u32 filter); | ||||
| int wl12xx_hw_init_phy_config(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_beacon_filter(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_pta(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_energy_detection(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_beacon_broadcast(struct wl12xx *wl); | ||||
| int wl12xx_hw_init_power_auth(struct wl12xx *wl); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										1358
									
								
								drivers/net/wireless/wl12xx/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1358
									
								
								drivers/net/wireless/wl12xx/main.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										151
									
								
								drivers/net/wireless/wl12xx/ps.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								drivers/net/wireless/wl12xx/ps.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "reg.h" | ||||
| #include "ps.h" | ||||
| #include "spi.h" | ||||
| 
 | ||||
| #define WL12XX_WAKEUP_TIMEOUT 2000 | ||||
| 
 | ||||
| /* Routines to toggle sleep mode while in ELP */ | ||||
| void wl12xx_ps_elp_sleep(struct wl12xx *wl) | ||||
| { | ||||
| 	if (wl->elp || !wl->psm) | ||||
| 		return; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_PSM, "chip to elp"); | ||||
| 
 | ||||
| 	wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); | ||||
| 
 | ||||
| 	wl->elp = true; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_ps_elp_wakeup(struct wl12xx *wl) | ||||
| { | ||||
| 	unsigned long timeout; | ||||
| 	u32 elp_reg; | ||||
| 
 | ||||
| 	if (!wl->elp) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_PSM, "waking up chip from elp"); | ||||
| 
 | ||||
| 	timeout = jiffies + msecs_to_jiffies(WL12XX_WAKEUP_TIMEOUT); | ||||
| 
 | ||||
| 	wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); | ||||
| 
 | ||||
| 	elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * FIXME: we should wait for irq from chip but, as a temporary | ||||
| 	 * solution to simplify locking, let's poll instead | ||||
| 	 */ | ||||
| 	while (!(elp_reg & ELPCTRL_WLAN_READY)) { | ||||
| 		if (time_after(jiffies, timeout)) { | ||||
| 			wl12xx_error("elp wakeup timeout"); | ||||
| 			return -ETIMEDOUT; | ||||
| 		} | ||||
| 		msleep(1); | ||||
| 		elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_PSM, "wakeup time: %u ms", | ||||
| 		     jiffies_to_msecs(jiffies) - | ||||
| 		     (jiffies_to_msecs(timeout) - WL12XX_WAKEUP_TIMEOUT)); | ||||
| 
 | ||||
| 	wl->elp = false; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_ps_set_elp(struct wl12xx *wl, bool enable) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		wl12xx_debug(DEBUG_PSM, "sleep auth psm/elp"); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * FIXME: we should PSM_ELP, but because of firmware wakeup | ||||
| 		 * problems let's use only PSM_PS | ||||
| 		 */ | ||||
| 		ret = wl12xx_acx_sleep_auth(wl, WL12XX_PSM_PS); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		wl12xx_ps_elp_sleep(wl); | ||||
| 	} else { | ||||
| 		wl12xx_debug(DEBUG_PSM, "sleep auth cam"); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * When the target is in ELP, we can only | ||||
| 		 * access the ELP control register. Thus, | ||||
| 		 * we have to wake the target up before | ||||
| 		 * changing the power authorization. | ||||
| 		 */ | ||||
| 
 | ||||
| 		wl12xx_ps_elp_wakeup(wl); | ||||
| 
 | ||||
| 		ret = wl12xx_acx_sleep_auth(wl, WL12XX_PSM_CAM); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	switch (mode) { | ||||
| 	case STATION_POWER_SAVE_MODE: | ||||
| 		wl12xx_debug(DEBUG_PSM, "entering psm"); | ||||
| 		ret = wl12xx_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		ret = wl12xx_ps_set_elp(wl, true); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		wl->psm = 1; | ||||
| 		break; | ||||
| 	case STATION_ACTIVE_MODE: | ||||
| 	default: | ||||
| 		wl12xx_debug(DEBUG_PSM, "leaving psm"); | ||||
| 		ret = wl12xx_ps_set_elp(wl, false); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		ret = wl12xx_cmd_ps_mode(wl, STATION_ACTIVE_MODE); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		wl->psm = 0; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										36
									
								
								drivers/net/wireless/wl12xx/ps.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								drivers/net/wireless/wl12xx/ps.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| #ifndef __WL12XX_PS_H__ | ||||
| #define __WL12XX_PS_H__ | ||||
| 
 | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "acx.h" | ||||
| 
 | ||||
| int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode); | ||||
| void wl12xx_ps_elp_sleep(struct wl12xx *wl); | ||||
| int wl12xx_ps_elp_wakeup(struct wl12xx *wl); | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __WL12XX_PS_H__ */ | ||||
							
								
								
									
										745
									
								
								drivers/net/wireless/wl12xx/reg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										745
									
								
								drivers/net/wireless/wl12xx/reg.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,745 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __REG_H__ | ||||
| #define __REG_H__ | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| #include "wl12xx.h" | ||||
| 
 | ||||
| #define REGISTERS_BASE 0x00300000 | ||||
| #define DRPW_BASE      0x00310000 | ||||
| 
 | ||||
| #define REGISTERS_DOWN_SIZE 0x00008800 | ||||
| #define REGISTERS_WORK_SIZE 0x0000b000 | ||||
| 
 | ||||
| #define HW_ACCESS_ELP_CTRL_REG_ADDR         0x1FFFC | ||||
| 
 | ||||
| /* ELP register commands */ | ||||
| #define ELPCTRL_WAKE_UP             0x1 | ||||
| #define ELPCTRL_WAKE_UP_WLAN_READY  0x5 | ||||
| #define ELPCTRL_SLEEP               0x0 | ||||
| /* ELP WLAN_READY bit */ | ||||
| #define ELPCTRL_WLAN_READY          0x2 | ||||
| 
 | ||||
| /*
 | ||||
|  * Interrupt registers. | ||||
|  * 64 bit interrupt sources registers ws ced. | ||||
|  * sme interupts were removed and new ones were added. | ||||
|  * Order was changed. | ||||
|  */ | ||||
| #define FIQ_MASK                       (REGISTERS_BASE + 0x0400) | ||||
| #define FIQ_MASK_L                     (REGISTERS_BASE + 0x0400) | ||||
| #define FIQ_MASK_H                     (REGISTERS_BASE + 0x0404) | ||||
| #define FIQ_MASK_SET                   (REGISTERS_BASE + 0x0408) | ||||
| #define FIQ_MASK_SET_L                 (REGISTERS_BASE + 0x0408) | ||||
| #define FIQ_MASK_SET_H                 (REGISTERS_BASE + 0x040C) | ||||
| #define FIQ_MASK_CLR                   (REGISTERS_BASE + 0x0410) | ||||
| #define FIQ_MASK_CLR_L                 (REGISTERS_BASE + 0x0410) | ||||
| #define FIQ_MASK_CLR_H                 (REGISTERS_BASE + 0x0414) | ||||
| #define IRQ_MASK                       (REGISTERS_BASE + 0x0418) | ||||
| #define IRQ_MASK_L                     (REGISTERS_BASE + 0x0418) | ||||
| #define IRQ_MASK_H                     (REGISTERS_BASE + 0x041C) | ||||
| #define IRQ_MASK_SET                   (REGISTERS_BASE + 0x0420) | ||||
| #define IRQ_MASK_SET_L                 (REGISTERS_BASE + 0x0420) | ||||
| #define IRQ_MASK_SET_H                 (REGISTERS_BASE + 0x0424) | ||||
| #define IRQ_MASK_CLR                   (REGISTERS_BASE + 0x0428) | ||||
| #define IRQ_MASK_CLR_L                 (REGISTERS_BASE + 0x0428) | ||||
| #define IRQ_MASK_CLR_H                 (REGISTERS_BASE + 0x042C) | ||||
| #define ECPU_MASK                      (REGISTERS_BASE + 0x0448) | ||||
| #define FIQ_STS_L                      (REGISTERS_BASE + 0x044C) | ||||
| #define FIQ_STS_H                      (REGISTERS_BASE + 0x0450) | ||||
| #define IRQ_STS_L                      (REGISTERS_BASE + 0x0454) | ||||
| #define IRQ_STS_H                      (REGISTERS_BASE + 0x0458) | ||||
| #define INT_STS_ND                     (REGISTERS_BASE + 0x0464) | ||||
| #define INT_STS_RAW_L                  (REGISTERS_BASE + 0x0464) | ||||
| #define INT_STS_RAW_H                  (REGISTERS_BASE + 0x0468) | ||||
| #define INT_STS_CLR                    (REGISTERS_BASE + 0x04B4) | ||||
| #define INT_STS_CLR_L                  (REGISTERS_BASE + 0x04B4) | ||||
| #define INT_STS_CLR_H                  (REGISTERS_BASE + 0x04B8) | ||||
| #define INT_ACK                        (REGISTERS_BASE + 0x046C) | ||||
| #define INT_ACK_L                      (REGISTERS_BASE + 0x046C) | ||||
| #define INT_ACK_H                      (REGISTERS_BASE + 0x0470) | ||||
| #define INT_TRIG                       (REGISTERS_BASE + 0x0474) | ||||
| #define INT_TRIG_L                     (REGISTERS_BASE + 0x0474) | ||||
| #define INT_TRIG_H                     (REGISTERS_BASE + 0x0478) | ||||
| #define HOST_STS_L                     (REGISTERS_BASE + 0x045C) | ||||
| #define HOST_STS_H                     (REGISTERS_BASE + 0x0460) | ||||
| #define HOST_MASK                      (REGISTERS_BASE + 0x0430) | ||||
| #define HOST_MASK_L                    (REGISTERS_BASE + 0x0430) | ||||
| #define HOST_MASK_H                    (REGISTERS_BASE + 0x0434) | ||||
| #define HOST_MASK_SET                  (REGISTERS_BASE + 0x0438) | ||||
| #define HOST_MASK_SET_L                (REGISTERS_BASE + 0x0438) | ||||
| #define HOST_MASK_SET_H                (REGISTERS_BASE + 0x043C) | ||||
| #define HOST_MASK_CLR                  (REGISTERS_BASE + 0x0440) | ||||
| #define HOST_MASK_CLR_L                (REGISTERS_BASE + 0x0440) | ||||
| #define HOST_MASK_CLR_H                (REGISTERS_BASE + 0x0444) | ||||
| 
 | ||||
| /* Host Interrupts*/ | ||||
| #define HINT_MASK                      (REGISTERS_BASE + 0x0494) | ||||
| #define HINT_MASK_SET                  (REGISTERS_BASE + 0x0498) | ||||
| #define HINT_MASK_CLR                  (REGISTERS_BASE + 0x049C) | ||||
| #define HINT_STS_ND_MASKED             (REGISTERS_BASE + 0x04A0) | ||||
| /*1150 spec calls this HINT_STS_RAW*/ | ||||
| #define HINT_STS_ND		       (REGISTERS_BASE + 0x04B0) | ||||
| #define HINT_STS_CLR                   (REGISTERS_BASE + 0x04A4) | ||||
| #define HINT_ACK                       (REGISTERS_BASE + 0x04A8) | ||||
| #define HINT_TRIG                      (REGISTERS_BASE + 0x04AC) | ||||
| 
 | ||||
| /* Device Configuration registers*/ | ||||
| #define SOR_CFG                        (REGISTERS_BASE + 0x0800) | ||||
| #define ECPU_CTRL                      (REGISTERS_BASE + 0x0804) | ||||
| #define HI_CFG                         (REGISTERS_BASE + 0x0808) | ||||
| #define EE_START                       (REGISTERS_BASE + 0x080C) | ||||
| 
 | ||||
| #define CHIP_ID_B                      (REGISTERS_BASE + 0x5674) | ||||
| 
 | ||||
| #define CHIP_ID_1251_PG10	           (0x7010101) | ||||
| #define CHIP_ID_1251_PG11	           (0x7020101) | ||||
| #define CHIP_ID_1251_PG12	           (0x7030101) | ||||
| 
 | ||||
| #define ENABLE                         (REGISTERS_BASE + 0x5450) | ||||
| 
 | ||||
| /* Power Management registers */ | ||||
| #define ELP_CFG_MODE                   (REGISTERS_BASE + 0x5804) | ||||
| #define ELP_CMD                        (REGISTERS_BASE + 0x5808) | ||||
| #define PLL_CAL_TIME                   (REGISTERS_BASE + 0x5810) | ||||
| #define CLK_REQ_TIME                   (REGISTERS_BASE + 0x5814) | ||||
| #define CLK_BUF_TIME                   (REGISTERS_BASE + 0x5818) | ||||
| 
 | ||||
| #define CFG_PLL_SYNC_CNT               (REGISTERS_BASE + 0x5820) | ||||
| 
 | ||||
| /* Scratch Pad registers*/ | ||||
| #define SCR_PAD0                       (REGISTERS_BASE + 0x5608) | ||||
| #define SCR_PAD1                       (REGISTERS_BASE + 0x560C) | ||||
| #define SCR_PAD2                       (REGISTERS_BASE + 0x5610) | ||||
| #define SCR_PAD3                       (REGISTERS_BASE + 0x5614) | ||||
| #define SCR_PAD4                       (REGISTERS_BASE + 0x5618) | ||||
| #define SCR_PAD4_SET                   (REGISTERS_BASE + 0x561C) | ||||
| #define SCR_PAD4_CLR                   (REGISTERS_BASE + 0x5620) | ||||
| #define SCR_PAD5                       (REGISTERS_BASE + 0x5624) | ||||
| #define SCR_PAD5_SET                   (REGISTERS_BASE + 0x5628) | ||||
| #define SCR_PAD5_CLR                   (REGISTERS_BASE + 0x562C) | ||||
| #define SCR_PAD6                       (REGISTERS_BASE + 0x5630) | ||||
| #define SCR_PAD7                       (REGISTERS_BASE + 0x5634) | ||||
| #define SCR_PAD8                       (REGISTERS_BASE + 0x5638) | ||||
| #define SCR_PAD9                       (REGISTERS_BASE + 0x563C) | ||||
| 
 | ||||
| /* Spare registers*/ | ||||
| #define SPARE_A1                       (REGISTERS_BASE + 0x0994) | ||||
| #define SPARE_A2                       (REGISTERS_BASE + 0x0998) | ||||
| #define SPARE_A3                       (REGISTERS_BASE + 0x099C) | ||||
| #define SPARE_A4                       (REGISTERS_BASE + 0x09A0) | ||||
| #define SPARE_A5                       (REGISTERS_BASE + 0x09A4) | ||||
| #define SPARE_A6                       (REGISTERS_BASE + 0x09A8) | ||||
| #define SPARE_A7                       (REGISTERS_BASE + 0x09AC) | ||||
| #define SPARE_A8                       (REGISTERS_BASE + 0x09B0) | ||||
| #define SPARE_B1                       (REGISTERS_BASE + 0x5420) | ||||
| #define SPARE_B2                       (REGISTERS_BASE + 0x5424) | ||||
| #define SPARE_B3                       (REGISTERS_BASE + 0x5428) | ||||
| #define SPARE_B4                       (REGISTERS_BASE + 0x542C) | ||||
| #define SPARE_B5                       (REGISTERS_BASE + 0x5430) | ||||
| #define SPARE_B6                       (REGISTERS_BASE + 0x5434) | ||||
| #define SPARE_B7                       (REGISTERS_BASE + 0x5438) | ||||
| #define SPARE_B8                       (REGISTERS_BASE + 0x543C) | ||||
| 
 | ||||
| enum wl12xx_acx_int_reg { | ||||
| 	ACX_REG_INTERRUPT_TRIG, | ||||
| 	ACX_REG_INTERRUPT_TRIG_H, | ||||
| 
 | ||||
| /*=============================================
 | ||||
|   Host Interrupt Mask Register - 32bit (RW) | ||||
|   ------------------------------------------ | ||||
|   Setting a bit in this register masks the | ||||
|   corresponding interrupt to the host. | ||||
|   0 - RX0		- Rx first dubble buffer Data Interrupt | ||||
|   1 - TXD		- Tx Data Interrupt | ||||
|   2 - TXXFR		- Tx Transfer Interrupt | ||||
|   3 - RX1		- Rx second dubble buffer Data Interrupt | ||||
|   4 - RXXFR		- Rx Transfer Interrupt | ||||
|   5 - EVENT_A	- Event Mailbox interrupt | ||||
|   6 - EVENT_B	- Event Mailbox interrupt | ||||
|   7 - WNONHST	- Wake On Host Interrupt | ||||
|   8 - TRACE_A	- Debug Trace interrupt | ||||
|   9 - TRACE_B	- Debug Trace interrupt | ||||
|  10 - CDCMP		- Command Complete Interrupt | ||||
|  11 - | ||||
|  12 - | ||||
|  13 - | ||||
|  14 - ICOMP		- Initialization Complete Interrupt | ||||
|  16 - SG SE		- Soft Gemini - Sense enable interrupt | ||||
|  17 - SG SD		- Soft Gemini - Sense disable interrupt | ||||
|  18 -			- | ||||
|  19 -			- | ||||
|  20 -			- | ||||
|  21-			- | ||||
|  Default: 0x0001 | ||||
| *==============================================*/ | ||||
| 	ACX_REG_INTERRUPT_MASK, | ||||
| 
 | ||||
| /*=============================================
 | ||||
|   Host Interrupt Mask Set 16bit, (Write only) | ||||
|   ------------------------------------------ | ||||
|  Setting a bit in this register sets | ||||
|  the corresponding bin in ACX_HINT_MASK register | ||||
|  without effecting the mask | ||||
|  state of other bits (0 = no effect). | ||||
| ==============================================*/ | ||||
| 	ACX_REG_HINT_MASK_SET, | ||||
| 
 | ||||
| /*=============================================
 | ||||
|   Host Interrupt Mask Clear 16bit,(Write only) | ||||
|   ------------------------------------------ | ||||
|  Setting a bit in this register clears | ||||
|  the corresponding bin in ACX_HINT_MASK register | ||||
|  without effecting the mask | ||||
|  state of other bits (0 = no effect). | ||||
| =============================================*/ | ||||
| 	ACX_REG_HINT_MASK_CLR, | ||||
| 
 | ||||
| /*=============================================
 | ||||
|   Host Interrupt Status Nondestructive Read | ||||
|   16bit,(Read only) | ||||
|   ------------------------------------------ | ||||
|  The host can read this register to determine | ||||
|  which interrupts are active. | ||||
|  Reading this register doesn't | ||||
|  effect its content. | ||||
| =============================================*/ | ||||
| 	ACX_REG_INTERRUPT_NO_CLEAR, | ||||
| 
 | ||||
| /*=============================================
 | ||||
|   Host Interrupt Status Clear on Read  Register | ||||
|   16bit,(Read only) | ||||
|   ------------------------------------------ | ||||
|  The host can read this register to determine | ||||
|  which interrupts are active. | ||||
|  Reading this register clears it, | ||||
|  thus making all interrupts inactive. | ||||
| ==============================================*/ | ||||
| 	ACX_REG_INTERRUPT_CLEAR, | ||||
| 
 | ||||
| /*=============================================
 | ||||
|   Host Interrupt Acknowledge Register | ||||
|   16bit,(Write only) | ||||
|   ------------------------------------------ | ||||
|  The host can set individual bits in this | ||||
|  register to clear (acknowledge) the corresp. | ||||
|  interrupt status bits in the HINT_STS_CLR and | ||||
|  HINT_STS_ND registers, thus making the | ||||
|  assotiated interrupt inactive. (0-no effect) | ||||
| ==============================================*/ | ||||
| 	ACX_REG_INTERRUPT_ACK, | ||||
| 
 | ||||
| /*===============================================
 | ||||
|    Host Software Reset - 32bit RW | ||||
|  ------------------------------------------ | ||||
|     [31:1] Reserved | ||||
|     0  SOFT_RESET Soft Reset  - When this bit is set, | ||||
|     it holds the Wlan hardware in a soft reset state. | ||||
|     This reset disables all MAC and baseband processor | ||||
|     clocks except the CardBus/PCI interface clock. | ||||
|     It also initializes all MAC state machines except | ||||
|     the host interface. It does not reload the | ||||
|     contents of the EEPROM. When this bit is cleared | ||||
|     (not self-clearing), the Wlan hardware | ||||
|     exits the software reset state. | ||||
| ===============================================*/ | ||||
| 	ACX_REG_SLV_SOFT_RESET, | ||||
| 
 | ||||
| /*===============================================
 | ||||
|  EEPROM Burst Read Start  - 32bit RW | ||||
|  ------------------------------------------ | ||||
|  [31:1] Reserved | ||||
|  0  ACX_EE_START -  EEPROM Burst Read Start 0 | ||||
|  Setting this bit starts a burst read from | ||||
|  the external EEPROM. | ||||
|  If this bit is set (after reset) before an EEPROM read/write, | ||||
|  the burst read starts at EEPROM address 0. | ||||
|  Otherwise, it starts at the address | ||||
|  following the address of the previous access. | ||||
|  TheWlan hardware hardware clears this bit automatically. | ||||
| 
 | ||||
|  Default: 0x00000000 | ||||
| *================================================*/ | ||||
| 	ACX_REG_EE_START, | ||||
| 
 | ||||
| /* Embedded ARM CPU Control */ | ||||
| 
 | ||||
| /*===============================================
 | ||||
|  Halt eCPU   - 32bit RW | ||||
|  ------------------------------------------ | ||||
|  0 HALT_ECPU Halt Embedded CPU - This bit is the | ||||
|  compliment of bit 1 (MDATA2) in the SOR_CFG register. | ||||
|  During a hardware reset, this bit holds | ||||
|  the inverse of MDATA2. | ||||
|  When downloading firmware from the host, | ||||
|  set this bit (pull down MDATA2). | ||||
|  The host clears this bit after downloading the firmware into | ||||
|  zero-wait-state SSRAM. | ||||
|  When loading firmware from Flash, clear this bit (pull up MDATA2) | ||||
|  so that the eCPU can run the bootloader code in Flash | ||||
|  HALT_ECPU eCPU State | ||||
|  -------------------- | ||||
|  1 halt eCPU | ||||
|  0 enable eCPU | ||||
|  ===============================================*/ | ||||
| 	ACX_REG_ECPU_CONTROL, | ||||
| 
 | ||||
| 	ACX_REG_TABLE_LEN | ||||
| }; | ||||
| 
 | ||||
| #define ACX_SLV_SOFT_RESET_BIT   BIT(1) | ||||
| #define ACX_REG_EEPROM_START_BIT BIT(1) | ||||
| 
 | ||||
| /* Command/Information Mailbox Pointers */ | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   Command Mailbox Pointer - 32bit RW | ||||
|  ------------------------------------------ | ||||
|  This register holds the start address of | ||||
|  the command mailbox located in the Wlan hardware memory. | ||||
|  The host must read this pointer after a reset to | ||||
|  find the location of the command mailbox. | ||||
|  The Wlan hardware initializes the command mailbox | ||||
|  pointer with the default address of the command mailbox. | ||||
|  The command mailbox pointer is not valid until after | ||||
|  the host receives the Init Complete interrupt from | ||||
|  the Wlan hardware. | ||||
|  ===============================================*/ | ||||
| #define REG_COMMAND_MAILBOX_PTR				(SCR_PAD0) | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   Information Mailbox Pointer - 32bit RW | ||||
|  ------------------------------------------ | ||||
|  This register holds the start address of | ||||
|  the information mailbox located in the Wlan hardware memory. | ||||
|  The host must read this pointer after a reset to find | ||||
|  the location of the information mailbox. | ||||
|  The Wlan hardware initializes the information mailbox pointer | ||||
|  with the default address of the information mailbox. | ||||
|  The information mailbox pointer is not valid | ||||
|  until after the host receives the Init Complete interrupt from | ||||
|  the Wlan hardware. | ||||
|  ===============================================*/ | ||||
| #define REG_EVENT_MAILBOX_PTR				(SCR_PAD1) | ||||
| 
 | ||||
| 
 | ||||
| /* Misc */ | ||||
| 
 | ||||
| #define REG_ENABLE_TX_RX				(ENABLE) | ||||
| /*
 | ||||
|  * Rx configuration (filter) information element | ||||
|  * --------------------------------------------- | ||||
|  */ | ||||
| #define REG_RX_CONFIG				(RX_CFG) | ||||
| #define REG_RX_FILTER				(RX_FILTER_CFG) | ||||
| 
 | ||||
| 
 | ||||
| #define RX_CFG_ENABLE_PHY_HEADER_PLCP	 0x0002 | ||||
| 
 | ||||
| /* promiscuous - receives all valid frames */ | ||||
| #define RX_CFG_PROMISCUOUS		 0x0008 | ||||
| 
 | ||||
| /* receives frames from any BSSID */ | ||||
| #define RX_CFG_BSSID			 0x0020 | ||||
| 
 | ||||
| /* receives frames destined to any MAC address */ | ||||
| #define RX_CFG_MAC			 0x0010 | ||||
| 
 | ||||
| #define RX_CFG_ENABLE_ONLY_MY_DEST_MAC	 0x0010 | ||||
| #define RX_CFG_ENABLE_ANY_DEST_MAC	 0x0000 | ||||
| #define RX_CFG_ENABLE_ONLY_MY_BSSID	 0x0020 | ||||
| #define RX_CFG_ENABLE_ANY_BSSID		 0x0000 | ||||
| 
 | ||||
| /* discards all broadcast frames */ | ||||
| #define RX_CFG_DISABLE_BCAST		 0x0200 | ||||
| 
 | ||||
| #define RX_CFG_ENABLE_ONLY_MY_SSID	 0x0400 | ||||
| #define RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR 0x0800 | ||||
| #define RX_CFG_COPY_RX_STATUS		 0x2000 | ||||
| #define RX_CFG_TSF			 0x10000 | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_ANY_DST_MY_BSS	 (RX_CFG_ENABLE_ANY_DEST_MAC | \ | ||||
| 					  RX_CFG_ENABLE_ONLY_MY_BSSID) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_MY_DST_ANY_BSS	 (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\ | ||||
| 					  | RX_CFG_ENABLE_ANY_BSSID) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_ANY_DST_ANY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \ | ||||
| 					  RX_CFG_ENABLE_ANY_BSSID) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_MY_DST_MY_BSS	 (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\ | ||||
| 					  | RX_CFG_ENABLE_ONLY_MY_BSSID) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_FOR_SCAN  (RX_CFG_ENABLE_PHY_HEADER_PLCP \ | ||||
| 				    | RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR \ | ||||
| 				    | RX_CFG_COPY_RX_STATUS | RX_CFG_TSF) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_FOR_MEASUREMENT (RX_CFG_ENABLE_ANY_DEST_MAC) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_FOR_JOIN	 (RX_CFG_ENABLE_ONLY_MY_BSSID | \ | ||||
| 					  RX_CFG_ENABLE_ONLY_MY_DEST_MAC) | ||||
| 
 | ||||
| #define RX_CONFIG_OPTION_FOR_IBSS_JOIN   (RX_CFG_ENABLE_ONLY_MY_SSID | \ | ||||
| 					  RX_CFG_ENABLE_ONLY_MY_DEST_MAC) | ||||
| 
 | ||||
| #define RX_FILTER_OPTION_DEF	      (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\ | ||||
| 				       | CFG_RX_CTL_EN | CFG_RX_BCN_EN\ | ||||
| 				       | CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) | ||||
| 
 | ||||
| #define RX_FILTER_OPTION_FILTER_ALL	 0 | ||||
| 
 | ||||
| #define RX_FILTER_OPTION_DEF_PRSP_BCN  (CFG_RX_PRSP_EN | CFG_RX_MGMT_EN\ | ||||
| 					| CFG_RX_RCTS_ACK | CFG_RX_BCN_EN) | ||||
| 
 | ||||
| #define RX_FILTER_OPTION_JOIN	     (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\ | ||||
| 				      | CFG_RX_BCN_EN | CFG_RX_AUTH_EN\ | ||||
| 				      | CFG_RX_ASSOC_EN | CFG_RX_RCTS_ACK\ | ||||
| 				      | CFG_RX_PRSP_EN) | ||||
| 
 | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   Phy regs | ||||
|  ===============================================*/ | ||||
| #define ACX_PHY_ADDR_REG                SBB_ADDR | ||||
| #define ACX_PHY_DATA_REG                SBB_DATA | ||||
| #define ACX_PHY_CTRL_REG                SBB_CTL | ||||
| #define ACX_PHY_REG_WR_MASK             0x00000001ul | ||||
| #define ACX_PHY_REG_RD_MASK             0x00000002ul | ||||
| 
 | ||||
| 
 | ||||
| /*===============================================
 | ||||
|  EEPROM Read/Write Request 32bit RW | ||||
|  ------------------------------------------ | ||||
|  1 EE_READ - EEPROM Read Request 1 - Setting this bit | ||||
|  loads a single byte of data into the EE_DATA | ||||
|  register from the EEPROM location specified in | ||||
|  the EE_ADDR register. | ||||
|  The Wlan hardware hardware clears this bit automatically. | ||||
|  EE_DATA is valid when this bit is cleared. | ||||
| 
 | ||||
|  0 EE_WRITE  - EEPROM Write Request  - Setting this bit | ||||
|  writes a single byte of data from the EE_DATA register into the | ||||
|  EEPROM location specified in the EE_ADDR register. | ||||
|  The Wlan hardware hardware clears this bit automatically. | ||||
| *===============================================*/ | ||||
| #define ACX_EE_CTL_REG                      EE_CTL | ||||
| #define EE_WRITE                            0x00000001ul | ||||
| #define EE_READ                             0x00000002ul | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   EEPROM Address  - 32bit RW | ||||
|   ------------------------------------------ | ||||
|   This register specifies the address | ||||
|   within the EEPROM from/to which to read/write data. | ||||
|   ===============================================*/ | ||||
| #define ACX_EE_ADDR_REG                     EE_ADDR | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   EEPROM Data  - 32bit RW | ||||
|   ------------------------------------------ | ||||
|   This register either holds the read 8 bits of | ||||
|   data from the EEPROM or the write data | ||||
|   to be written to the EEPROM. | ||||
|   ===============================================*/ | ||||
| #define ACX_EE_DATA_REG                     EE_DATA | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   EEPROM Base Address  - 32bit RW | ||||
|   ------------------------------------------ | ||||
|   This register holds the upper nine bits | ||||
|   [23:15] of the 24-bit Wlan hardware memory | ||||
|   address for burst reads from EEPROM accesses. | ||||
|   The EEPROM provides the lower 15 bits of this address. | ||||
|   The MSB of the address from the EEPROM is ignored. | ||||
|   ===============================================*/ | ||||
| #define ACX_EE_CFG                          EE_CFG | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   GPIO Output Values  -32bit, RW | ||||
|   ------------------------------------------ | ||||
|   [31:16]  Reserved | ||||
|   [15: 0]  Specify the output values (at the output driver inputs) for | ||||
|   GPIO[15:0], respectively. | ||||
|   ===============================================*/ | ||||
| #define ACX_GPIO_OUT_REG            GPIO_OUT | ||||
| #define ACX_MAX_GPIO_LINES          15 | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   Contention window  -32bit, RW | ||||
|   ------------------------------------------ | ||||
|   [31:26]  Reserved | ||||
|   [25:16]  Max (0x3ff) | ||||
|   [15:07]  Reserved | ||||
|   [06:00]  Current contention window value - default is 0x1F | ||||
|   ===============================================*/ | ||||
| #define ACX_CONT_WIND_CFG_REG    CONT_WIND_CFG | ||||
| #define ACX_CONT_WIND_MIN_MASK   0x0000007f | ||||
| #define ACX_CONT_WIND_MAX        0x03ff0000 | ||||
| 
 | ||||
| /*
 | ||||
|  * Indirect slave register/memory registers | ||||
|  * ---------------------------------------- | ||||
|  */ | ||||
| #define HW_SLAVE_REG_ADDR_REG		0x00000004 | ||||
| #define HW_SLAVE_REG_DATA_REG		0x00000008 | ||||
| #define HW_SLAVE_REG_CTRL_REG		0x0000000c | ||||
| 
 | ||||
| #define SLAVE_AUTO_INC				0x00010000 | ||||
| #define SLAVE_NO_AUTO_INC			0x00000000 | ||||
| #define SLAVE_HOST_LITTLE_ENDIAN	0x00000000 | ||||
| 
 | ||||
| #define HW_SLAVE_MEM_ADDR_REG		SLV_MEM_ADDR | ||||
| #define HW_SLAVE_MEM_DATA_REG		SLV_MEM_DATA | ||||
| #define HW_SLAVE_MEM_CTRL_REG		SLV_MEM_CTL | ||||
| #define HW_SLAVE_MEM_ENDIAN_REG		SLV_END_CTL | ||||
| 
 | ||||
| #define HW_FUNC_EVENT_INT_EN		0x8000 | ||||
| #define HW_FUNC_EVENT_MASK_REG		0x00000034 | ||||
| 
 | ||||
| #define ACX_MAC_TIMESTAMP_REG	(MAC_TIMESTAMP) | ||||
| 
 | ||||
| /*===============================================
 | ||||
|   HI_CFG Interface Configuration Register Values | ||||
|   ------------------------------------------ | ||||
|   ===============================================*/ | ||||
| #define HI_CFG_UART_ENABLE          0x00000004 | ||||
| #define HI_CFG_RST232_ENABLE        0x00000008 | ||||
| #define HI_CFG_CLOCK_REQ_SELECT     0x00000010 | ||||
| #define HI_CFG_HOST_INT_ENABLE      0x00000020 | ||||
| #define HI_CFG_VLYNQ_OUTPUT_ENABLE  0x00000040 | ||||
| #define HI_CFG_HOST_INT_ACTIVE_LOW  0x00000080 | ||||
| #define HI_CFG_UART_TX_OUT_GPIO_15  0x00000100 | ||||
| #define HI_CFG_UART_TX_OUT_GPIO_14  0x00000200 | ||||
| #define HI_CFG_UART_TX_OUT_GPIO_7   0x00000400 | ||||
| 
 | ||||
| /*
 | ||||
|  * NOTE: USE_ACTIVE_HIGH compilation flag should be defined in makefile | ||||
|  *       for platforms using active high interrupt level | ||||
|  */ | ||||
| #ifdef USE_ACTIVE_HIGH | ||||
| #define HI_CFG_DEF_VAL              \ | ||||
| 	(HI_CFG_UART_ENABLE |        \ | ||||
| 	HI_CFG_RST232_ENABLE |      \ | ||||
| 	HI_CFG_CLOCK_REQ_SELECT |   \ | ||||
| 	HI_CFG_HOST_INT_ENABLE) | ||||
| #else | ||||
| #define HI_CFG_DEF_VAL              \ | ||||
| 	(HI_CFG_UART_ENABLE |        \ | ||||
| 	HI_CFG_RST232_ENABLE |      \ | ||||
| 	HI_CFG_CLOCK_REQ_SELECT |   \ | ||||
| 	HI_CFG_HOST_INT_ENABLE) | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #define REF_FREQ_19_2                       0 | ||||
| #define REF_FREQ_26_0                       1 | ||||
| #define REF_FREQ_38_4                       2 | ||||
| #define REF_FREQ_40_0                       3 | ||||
| #define REF_FREQ_33_6                       4 | ||||
| #define REF_FREQ_NUM                        5 | ||||
| 
 | ||||
| #define LUT_PARAM_INTEGER_DIVIDER           0 | ||||
| #define LUT_PARAM_FRACTIONAL_DIVIDER        1 | ||||
| #define LUT_PARAM_ATTN_BB                   2 | ||||
| #define LUT_PARAM_ALPHA_BB                  3 | ||||
| #define LUT_PARAM_STOP_TIME_BB              4 | ||||
| #define LUT_PARAM_BB_PLL_LOOP_FILTER        5 | ||||
| #define LUT_PARAM_NUM                       6 | ||||
| 
 | ||||
| #define ACX_EEPROMLESS_IND_REG              (SCR_PAD4) | ||||
| #define USE_EEPROM                          0 | ||||
| #define SOFT_RESET_MAX_TIME                 1000000 | ||||
| #define SOFT_RESET_STALL_TIME               1000 | ||||
| #define NVS_DATA_BUNDARY_ALIGNMENT          4 | ||||
| 
 | ||||
| 
 | ||||
| /* Firmware image load chunk size */ | ||||
| #define CHUNK_SIZE          512 | ||||
| 
 | ||||
| /* Firmware image header size */ | ||||
| #define FW_HDR_SIZE 8 | ||||
| 
 | ||||
| #define ECPU_CONTROL_HALT					0x00000101 | ||||
| 
 | ||||
| 
 | ||||
| /******************************************************************************
 | ||||
| 
 | ||||
|     CHANNELS, BAND & REG DOMAINS definitions | ||||
| 
 | ||||
| ******************************************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| enum { | ||||
| 	RADIO_BAND_2_4GHZ = 0,  /* 2.4 Ghz band */ | ||||
| 	RADIO_BAND_5GHZ = 1,    /* 5 Ghz band */ | ||||
| 	RADIO_BAND_JAPAN_4_9_GHZ = 2, | ||||
| 	DEFAULT_BAND = RADIO_BAND_2_4GHZ, | ||||
| 	INVALID_BAND = 0xFE, | ||||
| 	MAX_RADIO_BANDS = 0xFF | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	NO_RATE      = 0, | ||||
| 	RATE_1MBPS   = 0x0A, | ||||
| 	RATE_2MBPS   = 0x14, | ||||
| 	RATE_5_5MBPS = 0x37, | ||||
| 	RATE_6MBPS   = 0x0B, | ||||
| 	RATE_9MBPS   = 0x0F, | ||||
| 	RATE_11MBPS  = 0x6E, | ||||
| 	RATE_12MBPS  = 0x0A, | ||||
| 	RATE_18MBPS  = 0x0E, | ||||
| 	RATE_22MBPS  = 0xDC, | ||||
| 	RATE_24MBPS  = 0x09, | ||||
| 	RATE_36MBPS  = 0x0D, | ||||
| 	RATE_48MBPS  = 0x08, | ||||
| 	RATE_54MBPS  = 0x0C | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	RATE_INDEX_1MBPS   =  0, | ||||
| 	RATE_INDEX_2MBPS   =  1, | ||||
| 	RATE_INDEX_5_5MBPS =  2, | ||||
| 	RATE_INDEX_6MBPS   =  3, | ||||
| 	RATE_INDEX_9MBPS   =  4, | ||||
| 	RATE_INDEX_11MBPS  =  5, | ||||
| 	RATE_INDEX_12MBPS  =  6, | ||||
| 	RATE_INDEX_18MBPS  =  7, | ||||
| 	RATE_INDEX_22MBPS  =  8, | ||||
| 	RATE_INDEX_24MBPS  =  9, | ||||
| 	RATE_INDEX_36MBPS  =  10, | ||||
| 	RATE_INDEX_48MBPS  =  11, | ||||
| 	RATE_INDEX_54MBPS  =  12, | ||||
| 	RATE_INDEX_MAX     =  RATE_INDEX_54MBPS, | ||||
| 	MAX_RATE_INDEX, | ||||
| 	INVALID_RATE_INDEX = MAX_RATE_INDEX, | ||||
| 	RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	RATE_MASK_1MBPS = 0x1, | ||||
| 	RATE_MASK_2MBPS = 0x2, | ||||
| 	RATE_MASK_5_5MBPS = 0x4, | ||||
| 	RATE_MASK_11MBPS = 0x20, | ||||
| }; | ||||
| 
 | ||||
| #define SHORT_PREAMBLE_BIT   BIT(0) /* CCK or Barker depending on the rate */ | ||||
| #define OFDM_RATE_BIT        BIT(6) | ||||
| #define PBCC_RATE_BIT        BIT(7) | ||||
| 
 | ||||
| enum { | ||||
| 	CCK_LONG = 0, | ||||
| 	CCK_SHORT = SHORT_PREAMBLE_BIT, | ||||
| 	PBCC_LONG = PBCC_RATE_BIT, | ||||
| 	PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT, | ||||
| 	OFDM = OFDM_RATE_BIT | ||||
| }; | ||||
| 
 | ||||
| /******************************************************************************
 | ||||
| 
 | ||||
| Transmit-Descriptor RATE-SET field definitions... | ||||
| 
 | ||||
| Define a new "Rate-Set" for TX path that incorporates the | ||||
| Rate & Modulation info into a single 16-bit field. | ||||
| 
 | ||||
| TxdRateSet_t: | ||||
| b15   - Indicates Preamble type (1=SHORT, 0=LONG). | ||||
| 	Notes: | ||||
| 	Must be LONG (0) for 1Mbps rate. | ||||
| 	Does not apply (set to 0) for RevG-OFDM rates. | ||||
| b14   - Indicates PBCC encoding (1=PBCC, 0=not). | ||||
| 	Notes: | ||||
| 	Does not apply (set to 0) for rates 1 and 2 Mbps. | ||||
| 	Does not apply (set to 0) for RevG-OFDM rates. | ||||
| b13    - Unused (set to 0). | ||||
| b12-b0 - Supported Rate indicator bits as defined below. | ||||
| 
 | ||||
| ******************************************************************************/ | ||||
| 
 | ||||
| 
 | ||||
| #define TNETW1251_CHIP_ID_PG1_0         0x07010101 | ||||
| #define TNETW1251_CHIP_ID_PG1_1         0x07020101 | ||||
| #define TNETW1251_CHIP_ID_PG1_2	        0x07030101 | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
| 
 | ||||
|     Interrupt Trigger Register (Host -> WiLink) | ||||
| 
 | ||||
| **************************************************************************/ | ||||
| 
 | ||||
| /* Hardware to Embedded CPU Interrupts - first 32-bit register set */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Host Command Interrupt. Setting this bit masks | ||||
|  * the interrupt that the host issues to inform | ||||
|  * the FW that it has sent a command | ||||
|  * to the Wlan hardware Command Mailbox. | ||||
|  */ | ||||
| #define INTR_TRIG_CMD       BIT(0) | ||||
| 
 | ||||
| /*
 | ||||
|  * Host Event Acknowlegde Interrupt. The host | ||||
|  * sets this bit to acknowledge that it received | ||||
|  * the unsolicited information from the event | ||||
|  * mailbox. | ||||
|  */ | ||||
| #define INTR_TRIG_EVENT_ACK BIT(1) | ||||
| 
 | ||||
| /*
 | ||||
|  * The host sets this bit to inform the Wlan | ||||
|  * FW that a TX packet is in the XFER | ||||
|  * Buffer #0. | ||||
|  */ | ||||
| #define INTR_TRIG_TX_PROC0 BIT(2) | ||||
| 
 | ||||
| /*
 | ||||
|  * The host sets this bit to inform the FW | ||||
|  * that it read a packet from RX XFER | ||||
|  * Buffer #0. | ||||
|  */ | ||||
| #define INTR_TRIG_RX_PROC0 BIT(3) | ||||
| 
 | ||||
| #define INTR_TRIG_DEBUG_ACK BIT(4) | ||||
| 
 | ||||
| #define INTR_TRIG_STATE_CHANGED BIT(5) | ||||
| 
 | ||||
| 
 | ||||
| /* Hardware to Embedded CPU Interrupts - second 32-bit register set */ | ||||
| 
 | ||||
| /*
 | ||||
|  * The host sets this bit to inform the FW | ||||
|  * that it read a packet from RX XFER | ||||
|  * Buffer #1. | ||||
|  */ | ||||
| #define INTR_TRIG_RX_PROC1 BIT(17) | ||||
| 
 | ||||
| /*
 | ||||
|  * The host sets this bit to inform the Wlan | ||||
|  * hardware that a TX packet is in the XFER | ||||
|  * Buffer #1. | ||||
|  */ | ||||
| #define INTR_TRIG_TX_PROC1 BIT(18) | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										208
									
								
								drivers/net/wireless/wl12xx/rx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								drivers/net/wireless/wl12xx/rx.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,208 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/skbuff.h> | ||||
| #include <net/mac80211.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "rx.h" | ||||
| 
 | ||||
| static void wl12xx_rx_header(struct wl12xx *wl, | ||||
| 			     struct wl12xx_rx_descriptor *desc) | ||||
| { | ||||
| 	u32 rx_packet_ring_addr; | ||||
| 
 | ||||
| 	rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr; | ||||
| 	if (wl->rx_current_buffer) | ||||
| 		rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; | ||||
| 
 | ||||
| 	wl12xx_spi_mem_read(wl, rx_packet_ring_addr, desc, | ||||
| 			    sizeof(struct wl12xx_rx_descriptor)); | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_rx_status(struct wl12xx *wl, | ||||
| 			     struct wl12xx_rx_descriptor *desc, | ||||
| 			     struct ieee80211_rx_status *status, | ||||
| 			     u8 beacon) | ||||
| { | ||||
| 	memset(status, 0, sizeof(struct ieee80211_rx_status)); | ||||
| 
 | ||||
| 	status->band = IEEE80211_BAND_2GHZ; | ||||
| 	status->mactime = desc->timestamp; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The rx status timestamp is a 32 bits value while the TSF is a | ||||
| 	 * 64 bits one. | ||||
| 	 * For IBSS merging, TSF is mandatory, so we have to get it | ||||
| 	 * somehow, so we ask for ACX_TSF_INFO. | ||||
| 	 * That could be moved to the get_tsf() hook, but unfortunately, | ||||
| 	 * this one must be atomic, while our SPI routines can sleep. | ||||
| 	 */ | ||||
| 	if ((wl->bss_type == BSS_TYPE_IBSS) && beacon) { | ||||
| 		u64 mactime; | ||||
| 		int ret; | ||||
| 		struct wl12xx_command cmd; | ||||
| 		struct acx_tsf_info *tsf_info; | ||||
| 
 | ||||
| 		memset(&cmd, 0, sizeof(cmd)); | ||||
| 
 | ||||
| 		ret = wl12xx_cmd_interrogate(wl, ACX_TSF_INFO, | ||||
| 					     sizeof(struct acx_tsf_info), | ||||
| 					     &cmd); | ||||
| 		if (ret < 0) { | ||||
| 			wl12xx_warning("ACX_FW_REV interrogate failed"); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		tsf_info = (struct acx_tsf_info *)&(cmd.parameters); | ||||
| 
 | ||||
| 		mactime = tsf_info->current_tsf_lsb | | ||||
| 			(tsf_info->current_tsf_msb << 31); | ||||
| 
 | ||||
| 		status->mactime = mactime; | ||||
| 	} | ||||
| 
 | ||||
| 	status->signal = desc->rssi; | ||||
| 	status->qual = (desc->rssi - WL12XX_RX_MIN_RSSI) * 100 / | ||||
| 		(WL12XX_RX_MAX_RSSI - WL12XX_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 | ||||
| 	 * the values don't make any sense | ||||
| 	 */ | ||||
| 	status->noise = desc->rssi - desc->snr / 2; | ||||
| 
 | ||||
| 	status->freq = ieee80211_channel_to_frequency(desc->channel); | ||||
| 
 | ||||
| 	status->flag |= RX_FLAG_TSFT; | ||||
| 
 | ||||
| 	if (desc->flags & RX_DESC_ENCRYPTION_MASK) { | ||||
| 		status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; | ||||
| 
 | ||||
| 		if (likely(!(desc->flags & RX_DESC_DECRYPT_FAIL))) | ||||
| 			status->flag |= RX_FLAG_DECRYPTED; | ||||
| 
 | ||||
| 		if (unlikely(desc->flags & RX_DESC_MIC_FAIL)) | ||||
| 			status->flag |= RX_FLAG_MMIC_ERROR; | ||||
| 	} | ||||
| 
 | ||||
| 	if (unlikely(!(desc->flags & RX_DESC_VALID_FCS))) | ||||
| 		status->flag |= RX_FLAG_FAILED_FCS_CRC; | ||||
| 
 | ||||
| 
 | ||||
| 	/* FIXME: set status->rate_idx */ | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_rx_body(struct wl12xx *wl, | ||||
| 			   struct wl12xx_rx_descriptor *desc) | ||||
| { | ||||
| 	struct sk_buff *skb; | ||||
| 	struct ieee80211_rx_status status; | ||||
| 	u8 *rx_buffer, beacon = 0; | ||||
| 	u16 length, *fc; | ||||
| 	u32 curr_id, last_id_inc, rx_packet_ring_addr; | ||||
| 
 | ||||
| 	length = WL12XX_RX_ALIGN(desc->length  - PLCP_HEADER_LENGTH); | ||||
| 	curr_id = (desc->flags & RX_DESC_SEQNUM_MASK) >> RX_DESC_PACKETID_SHIFT; | ||||
| 	last_id_inc = (wl->rx_last_id + 1) % (RX_MAX_PACKET_ID + 1); | ||||
| 
 | ||||
| 	if (last_id_inc != curr_id) { | ||||
| 		wl12xx_warning("curr ID:%d, last ID inc:%d", | ||||
| 			       curr_id, last_id_inc); | ||||
| 		wl->rx_last_id = curr_id; | ||||
| 	} else { | ||||
| 		wl->rx_last_id = last_id_inc; | ||||
| 	} | ||||
| 
 | ||||
| 	rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr + | ||||
| 		sizeof(struct wl12xx_rx_descriptor) + 20; | ||||
| 	if (wl->rx_current_buffer) | ||||
| 		rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; | ||||
| 
 | ||||
| 	skb = dev_alloc_skb(length); | ||||
| 	if (!skb) { | ||||
| 		wl12xx_error("Couldn't allocate RX frame"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	rx_buffer = skb_put(skb, length); | ||||
| 	wl12xx_spi_mem_read(wl, rx_packet_ring_addr, rx_buffer, length); | ||||
| 
 | ||||
| 	/* The actual lenght doesn't include the target's alignment */ | ||||
| 	skb->len = desc->length  - PLCP_HEADER_LENGTH; | ||||
| 
 | ||||
| 	fc = (u16 *)skb->data; | ||||
| 
 | ||||
| 	if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) | ||||
| 		beacon = 1; | ||||
| 
 | ||||
| 	wl12xx_rx_status(wl, desc, &status, beacon); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, | ||||
| 		     beacon ? "beacon" : ""); | ||||
| 
 | ||||
| 	ieee80211_rx(wl->hw, skb, &status); | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_rx_ack(struct wl12xx *wl) | ||||
| { | ||||
| 	u32 data, addr; | ||||
| 
 | ||||
| 	if (wl->rx_current_buffer) { | ||||
| 		addr = ACX_REG_INTERRUPT_TRIG_H; | ||||
| 		data = INTR_TRIG_RX_PROC1; | ||||
| 	} else { | ||||
| 		addr = ACX_REG_INTERRUPT_TRIG; | ||||
| 		data = INTR_TRIG_RX_PROC0; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, addr, data); | ||||
| 
 | ||||
| 	/* Toggle buffer ring */ | ||||
| 	wl->rx_current_buffer = !wl->rx_current_buffer; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void wl12xx_rx(struct wl12xx *wl) | ||||
| { | ||||
| 	struct wl12xx_rx_descriptor rx_desc; | ||||
| 
 | ||||
| 	if (wl->state != WL12XX_STATE_ON) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* We first read the frame's header */ | ||||
| 	wl12xx_rx_header(wl, &rx_desc); | ||||
| 
 | ||||
| 	/* Now we can read the body */ | ||||
| 	wl12xx_rx_body(wl, &rx_desc); | ||||
| 
 | ||||
| 	/* Finally, we need to ACK the RX */ | ||||
| 	wl12xx_rx_ack(wl); | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
							
								
								
									
										122
									
								
								drivers/net/wireless/wl12xx/rx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								drivers/net/wireless/wl12xx/rx.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_RX_H__ | ||||
| #define __WL12XX_RX_H__ | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * RX PATH | ||||
|  * | ||||
|  * The Rx path uses a double buffer and an rx_contro structure, each located | ||||
|  * at a fixed address in the device memory. The host keeps track of which | ||||
|  * buffer is available and alternates between them on a per packet basis. | ||||
|  * The size of each of the two buffers is large enough to hold the longest | ||||
|  * 802.3 packet. | ||||
|  * The RX path goes like that: | ||||
|  * 1) The target generates an interrupt each time a new packet is received. | ||||
|  *   There are 2 RX interrupts, one for each buffer. | ||||
|  * 2) The host reads the received packet from one of the double buffers. | ||||
|  * 3) The host triggers a target interrupt. | ||||
|  * 4) The target prepares the next RX packet. | ||||
|  */ | ||||
| 
 | ||||
| #define WL12XX_RX_MAX_RSSI -30 | ||||
| #define WL12XX_RX_MIN_RSSI -95 | ||||
| 
 | ||||
| #define WL12XX_RX_ALIGN_TO 4 | ||||
| #define WL12XX_RX_ALIGN(len) (((len) + WL12XX_RX_ALIGN_TO - 1) & \ | ||||
| 			     ~(WL12XX_RX_ALIGN_TO - 1)) | ||||
| 
 | ||||
| #define SHORT_PREAMBLE_BIT   BIT(0) | ||||
| #define OFDM_RATE_BIT        BIT(6) | ||||
| #define PBCC_RATE_BIT        BIT(7) | ||||
| 
 | ||||
| #define PLCP_HEADER_LENGTH 8 | ||||
| #define RX_DESC_PACKETID_SHIFT 11 | ||||
| #define RX_MAX_PACKET_ID 3 | ||||
| 
 | ||||
| #define RX_DESC_VALID_FCS         0x0001 | ||||
| #define RX_DESC_MATCH_RXADDR1     0x0002 | ||||
| #define RX_DESC_MCAST             0x0004 | ||||
| #define RX_DESC_STAINTIM          0x0008 | ||||
| #define RX_DESC_VIRTUAL_BM        0x0010 | ||||
| #define RX_DESC_BCAST             0x0020 | ||||
| #define RX_DESC_MATCH_SSID        0x0040 | ||||
| #define RX_DESC_MATCH_BSSID       0x0080 | ||||
| #define RX_DESC_ENCRYPTION_MASK   0x0300 | ||||
| #define RX_DESC_MEASURMENT        0x0400 | ||||
| #define RX_DESC_SEQNUM_MASK       0x1800 | ||||
| #define	RX_DESC_MIC_FAIL	  0x2000 | ||||
| #define	RX_DESC_DECRYPT_FAIL	  0x4000 | ||||
| 
 | ||||
| struct wl12xx_rx_descriptor { | ||||
| 	u32 timestamp; /* In microseconds */ | ||||
| 	u16 length; /* Paylod length, including headers */ | ||||
| 	u16 flags; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 0 - 802.11 | ||||
| 	 * 1 - 802.3 | ||||
| 	 * 2 - IP | ||||
| 	 * 3 - Raw Codec | ||||
| 	 */ | ||||
| 	u8 type; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Recevied Rate: | ||||
| 	 * 0x0A - 1MBPS | ||||
| 	 * 0x14 - 2MBPS | ||||
| 	 * 0x37 - 5_5MBPS | ||||
| 	 * 0x0B - 6MBPS | ||||
| 	 * 0x0F - 9MBPS | ||||
| 	 * 0x6E - 11MBPS | ||||
| 	 * 0x0A - 12MBPS | ||||
| 	 * 0x0E - 18MBPS | ||||
| 	 * 0xDC - 22MBPS | ||||
| 	 * 0x09 - 24MBPS | ||||
| 	 * 0x0D - 36MBPS | ||||
| 	 * 0x08 - 48MBPS | ||||
| 	 * 0x0C - 54MBPS | ||||
| 	 */ | ||||
| 	u8 rate; | ||||
| 
 | ||||
| 	u8 mod_pre; /* Modulation and preamble */ | ||||
| 	u8 channel; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 0 - 2.4 Ghz | ||||
| 	 * 1 - 5 Ghz | ||||
| 	 */ | ||||
| 	u8 band; | ||||
| 
 | ||||
| 	s8 rssi; /* in dB */ | ||||
| 	u8 rcpi; /* in dB */ | ||||
| 	u8 snr; /* in dB */ | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| void wl12xx_rx(struct wl12xx *wl); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										358
									
								
								drivers/net/wireless/wl12xx/spi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								drivers/net/wireless/wl12xx/spi.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/crc7.h> | ||||
| #include <linux/spi/spi.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "wl12xx_80211.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "ps.h" | ||||
| 
 | ||||
| static int wl12xx_translate_reg_addr(struct wl12xx *wl, int addr) | ||||
| { | ||||
| 	/* If the address is lower than REGISTERS_BASE, it means that this is
 | ||||
| 	 * a chip-specific register address, so look it up in the registers | ||||
| 	 * table */ | ||||
| 	if (addr < REGISTERS_BASE) { | ||||
| 		/* Make sure we don't go over the table */ | ||||
| 		if (addr >= ACX_REG_TABLE_LEN) { | ||||
| 			wl12xx_error("address out of range (%d)", addr); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		addr = wl->chip.acx_reg_table[addr]; | ||||
| 	} | ||||
| 
 | ||||
| 	return addr - wl->physical_reg_addr + wl->virtual_reg_addr; | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_translate_mem_addr(struct wl12xx *wl, int addr) | ||||
| { | ||||
| 	return addr - wl->physical_mem_addr + wl->virtual_mem_addr; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void wl12xx_spi_reset(struct wl12xx *wl) | ||||
| { | ||||
| 	u8 *cmd; | ||||
| 	struct spi_transfer t; | ||||
| 	struct spi_message m; | ||||
| 
 | ||||
| 	cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); | ||||
| 	if (!cmd) { | ||||
| 		wl12xx_error("could not allocate cmd for spi reset"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	memset(&t, 0, sizeof(t)); | ||||
| 	spi_message_init(&m); | ||||
| 
 | ||||
| 	memset(cmd, 0xff, WSPI_INIT_CMD_LEN); | ||||
| 
 | ||||
| 	t.tx_buf = cmd; | ||||
| 	t.len = WSPI_INIT_CMD_LEN; | ||||
| 	spi_message_add_tail(&t, &m); | ||||
| 
 | ||||
| 	spi_sync(wl->spi, &m); | ||||
| 
 | ||||
| 	wl12xx_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_spi_init(struct wl12xx *wl) | ||||
| { | ||||
| 	u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; | ||||
| 	struct spi_transfer t; | ||||
| 	struct spi_message m; | ||||
| 
 | ||||
| 	cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); | ||||
| 	if (!cmd) { | ||||
| 		wl12xx_error("could not allocate cmd for spi init"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	memset(crc, 0, sizeof(crc)); | ||||
| 	memset(&t, 0, sizeof(t)); | ||||
| 	spi_message_init(&m); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set WSPI_INIT_COMMAND | ||||
| 	 * the data is being send from the MSB to LSB | ||||
| 	 */ | ||||
| 	cmd[2] = 0xff; | ||||
| 	cmd[3] = 0xff; | ||||
| 	cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; | ||||
| 	cmd[0] = 0; | ||||
| 	cmd[7] = 0; | ||||
| 	cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; | ||||
| 	cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; | ||||
| 
 | ||||
| 	if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) | ||||
| 		cmd[5] |=  WSPI_INIT_CMD_DIS_FIXEDBUSY; | ||||
| 	else | ||||
| 		cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; | ||||
| 
 | ||||
| 	cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS | ||||
| 		| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; | ||||
| 
 | ||||
| 	crc[0] = cmd[1]; | ||||
| 	crc[1] = cmd[0]; | ||||
| 	crc[2] = cmd[7]; | ||||
| 	crc[3] = cmd[6]; | ||||
| 	crc[4] = cmd[5]; | ||||
| 
 | ||||
| 	cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; | ||||
| 	cmd[4] |= WSPI_INIT_CMD_END; | ||||
| 
 | ||||
| 	t.tx_buf = cmd; | ||||
| 	t.len = WSPI_INIT_CMD_LEN; | ||||
| 	spi_message_add_tail(&t, &m); | ||||
| 
 | ||||
| 	spi_sync(wl->spi, &m); | ||||
| 
 | ||||
| 	wl12xx_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); | ||||
| } | ||||
| 
 | ||||
| /* Set the SPI partitions to access the chip addresses
 | ||||
|  * | ||||
|  * There are two VIRTUAL (SPI) partitions (the memory partition and the | ||||
|  * registers partition), which are mapped to two different areas of the | ||||
|  * PHYSICAL (hardware) memory.  This function also makes other checks to | ||||
|  * ensure that the partitions are not overlapping.  In the diagram below, the | ||||
|  * memory partition comes before the register partition, but the opposite is | ||||
|  * also supported. | ||||
|  * | ||||
|  *                               PHYSICAL address | ||||
|  *                                     space | ||||
|  * | ||||
|  *                                    |    | | ||||
|  *                                 ...+----+--> mem_start | ||||
|  *          VIRTUAL address     ...   |    | | ||||
|  *               space       ...      |    | [PART_0] | ||||
|  *                        ...         |    | | ||||
|  * 0x00000000 <--+----+...         ...+----+--> mem_start + mem_size | ||||
|  *               |    |         ...   |    | | ||||
|  *               |MEM |      ...      |    | | ||||
|  *               |    |   ...         |    | | ||||
|  *  part_size <--+----+...            |    | {unused area) | ||||
|  *               |    |   ...         |    | | ||||
|  *               |REG |      ...      |    | | ||||
|  *  part_size    |    |         ...   |    | | ||||
|  *      +     <--+----+...         ...+----+--> reg_start | ||||
|  *  reg_size              ...         |    | | ||||
|  *                           ...      |    | [PART_1] | ||||
|  *                              ...   |    | | ||||
|  *                                 ...+----+--> reg_start + reg_size | ||||
|  *                                    |    | | ||||
|  * | ||||
|  */ | ||||
| void wl12xx_set_partition(struct wl12xx *wl, | ||||
| 			  u32 mem_start, u32 mem_size, | ||||
| 			  u32 reg_start, u32 reg_size) | ||||
| { | ||||
| 	u8 tx_buf[sizeof(u32) + 2 * sizeof(struct wl12xx_partition)]; | ||||
| 	struct wl12xx_partition *partition; | ||||
| 	struct spi_transfer t; | ||||
| 	struct spi_message m; | ||||
| 	u32 *cmd; | ||||
| 	size_t len; | ||||
| 	int addr; | ||||
| 
 | ||||
| 	spi_message_init(&m); | ||||
| 	memset(&t, 0, sizeof(t)); | ||||
| 	memset(tx_buf, 0, sizeof(tx_buf)); | ||||
| 
 | ||||
| 	cmd = (u32 *) tx_buf; | ||||
| 	partition = (struct wl12xx_partition *) (tx_buf + sizeof(u32)); | ||||
| 	addr = HW_ACCESS_PART0_SIZE_ADDR; | ||||
| 	len = 2 * sizeof(struct wl12xx_partition); | ||||
| 
 | ||||
| 	*cmd |= WSPI_CMD_WRITE; | ||||
| 	*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | ||||
| 	*cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", | ||||
| 		     mem_start, mem_size); | ||||
| 	wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", | ||||
| 		     reg_start, reg_size); | ||||
| 
 | ||||
| 	/* Make sure that the two partitions together don't exceed the
 | ||||
| 	 * address range */ | ||||
| 	if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { | ||||
| 		wl12xx_debug(DEBUG_SPI, "Total size exceeds maximum virtual" | ||||
| 			     " address range.  Truncating partition[0]."); | ||||
| 		mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; | ||||
| 		wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", | ||||
| 			     mem_start, mem_size); | ||||
| 		wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", | ||||
| 			     reg_start, reg_size); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((mem_start < reg_start) && | ||||
| 	    ((mem_start + mem_size) > reg_start)) { | ||||
| 		/* Guarantee that the memory partition doesn't overlap the
 | ||||
| 		 * registers partition */ | ||||
| 		wl12xx_debug(DEBUG_SPI, "End of partition[0] is " | ||||
| 			     "overlapping partition[1].  Adjusted."); | ||||
| 		mem_size = reg_start - mem_start; | ||||
| 		wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", | ||||
| 			     mem_start, mem_size); | ||||
| 		wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", | ||||
| 			     reg_start, reg_size); | ||||
| 	} else if ((reg_start < mem_start) && | ||||
| 		   ((reg_start + reg_size) > mem_start)) { | ||||
| 		/* Guarantee that the register partition doesn't overlap the
 | ||||
| 		 * memory partition */ | ||||
| 		wl12xx_debug(DEBUG_SPI, "End of partition[1] is" | ||||
| 			     " overlapping partition[0].  Adjusted."); | ||||
| 		reg_size = mem_start - reg_start; | ||||
| 		wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", | ||||
| 			     mem_start, mem_size); | ||||
| 		wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", | ||||
| 			     reg_start, reg_size); | ||||
| 	} | ||||
| 
 | ||||
| 	partition[0].start = mem_start; | ||||
| 	partition[0].size  = mem_size; | ||||
| 	partition[1].start = reg_start; | ||||
| 	partition[1].size  = reg_size; | ||||
| 
 | ||||
| 	wl->physical_mem_addr = mem_start; | ||||
| 	wl->physical_reg_addr = reg_start; | ||||
| 
 | ||||
| 	wl->virtual_mem_addr = 0; | ||||
| 	wl->virtual_reg_addr = mem_size; | ||||
| 
 | ||||
| 	t.tx_buf = tx_buf; | ||||
| 	t.len = sizeof(tx_buf); | ||||
| 	spi_message_add_tail(&t, &m); | ||||
| 
 | ||||
| 	spi_sync(wl->spi, &m); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_spi_read(struct wl12xx *wl, int addr, void *buf, | ||||
| 		     size_t len) | ||||
| { | ||||
| 	struct spi_transfer t[3]; | ||||
| 	struct spi_message m; | ||||
| 	char busy_buf[TNETWIF_READ_OFFSET_BYTES]; | ||||
| 	u32 cmd; | ||||
| 
 | ||||
| 	cmd = 0; | ||||
| 	cmd |= WSPI_CMD_READ; | ||||
| 	cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | ||||
| 	cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||||
| 
 | ||||
| 	spi_message_init(&m); | ||||
| 	memset(t, 0, sizeof(t)); | ||||
| 
 | ||||
| 	t[0].tx_buf = &cmd; | ||||
| 	t[0].len = 4; | ||||
| 	spi_message_add_tail(&t[0], &m); | ||||
| 
 | ||||
| 	/* Busy and non busy words read */ | ||||
| 	t[1].rx_buf = busy_buf; | ||||
| 	t[1].len = TNETWIF_READ_OFFSET_BYTES; | ||||
| 	spi_message_add_tail(&t[1], &m); | ||||
| 
 | ||||
| 	t[2].rx_buf = buf; | ||||
| 	t[2].len = len; | ||||
| 	spi_message_add_tail(&t[2], &m); | ||||
| 
 | ||||
| 	spi_sync(wl->spi, &m); | ||||
| 
 | ||||
| 	/* FIXME: check busy words */ | ||||
| 
 | ||||
| 	wl12xx_dump(DEBUG_SPI, "spi_read cmd -> ", &cmd, sizeof(cmd)); | ||||
| 	wl12xx_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_spi_write(struct wl12xx *wl, int addr, void *buf, | ||||
| 		      size_t len) | ||||
| { | ||||
| 	struct spi_transfer t[2]; | ||||
| 	struct spi_message m; | ||||
| 	u32 cmd; | ||||
| 
 | ||||
| 	cmd = 0; | ||||
| 	cmd |= WSPI_CMD_WRITE; | ||||
| 	cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; | ||||
| 	cmd |= addr & WSPI_CMD_BYTE_ADDR; | ||||
| 
 | ||||
| 	spi_message_init(&m); | ||||
| 	memset(t, 0, sizeof(t)); | ||||
| 
 | ||||
| 	t[0].tx_buf = &cmd; | ||||
| 	t[0].len = sizeof(cmd); | ||||
| 	spi_message_add_tail(&t[0], &m); | ||||
| 
 | ||||
| 	t[1].tx_buf = buf; | ||||
| 	t[1].len = len; | ||||
| 	spi_message_add_tail(&t[1], &m); | ||||
| 
 | ||||
| 	spi_sync(wl->spi, &m); | ||||
| 
 | ||||
| 	wl12xx_dump(DEBUG_SPI, "spi_write cmd -> ", &cmd, sizeof(cmd)); | ||||
| 	wl12xx_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_spi_mem_read(struct wl12xx *wl, int addr, void *buf, | ||||
| 			 size_t len) | ||||
| { | ||||
| 	int physical; | ||||
| 
 | ||||
| 	physical = wl12xx_translate_mem_addr(wl, addr); | ||||
| 
 | ||||
| 	wl12xx_spi_read(wl, physical, buf, len); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_spi_mem_write(struct wl12xx *wl, int addr, void *buf, | ||||
| 			  size_t len) | ||||
| { | ||||
| 	int physical; | ||||
| 
 | ||||
| 	physical = wl12xx_translate_mem_addr(wl, addr); | ||||
| 
 | ||||
| 	wl12xx_spi_write(wl, physical, buf, len); | ||||
| } | ||||
| 
 | ||||
| u32 wl12xx_mem_read32(struct wl12xx *wl, int addr) | ||||
| { | ||||
| 	return wl12xx_read32(wl, wl12xx_translate_mem_addr(wl, addr)); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_mem_write32(struct wl12xx *wl, int addr, u32 val) | ||||
| { | ||||
| 	wl12xx_write32(wl, wl12xx_translate_mem_addr(wl, addr), val); | ||||
| } | ||||
| 
 | ||||
| u32 wl12xx_reg_read32(struct wl12xx *wl, int addr) | ||||
| { | ||||
| 	return wl12xx_read32(wl, wl12xx_translate_reg_addr(wl, addr)); | ||||
| } | ||||
| 
 | ||||
| void wl12xx_reg_write32(struct wl12xx *wl, int addr, u32 val) | ||||
| { | ||||
| 	wl12xx_write32(wl, wl12xx_translate_reg_addr(wl, addr), val); | ||||
| } | ||||
							
								
								
									
										109
									
								
								drivers/net/wireless/wl12xx/spi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								drivers/net/wireless/wl12xx/spi.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,109 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_SPI_H__ | ||||
| #define __WL12XX_SPI_H__ | ||||
| 
 | ||||
| #include "cmd.h" | ||||
| #include "acx.h" | ||||
| #include "reg.h" | ||||
| 
 | ||||
| #define HW_ACCESS_MEMORY_MAX_RANGE		0x1FFC0 | ||||
| 
 | ||||
| #define HW_ACCESS_PART0_SIZE_ADDR           0x1FFC0 | ||||
| #define HW_ACCESS_PART0_START_ADDR          0x1FFC4 | ||||
| #define HW_ACCESS_PART1_SIZE_ADDR           0x1FFC8 | ||||
| #define HW_ACCESS_PART1_START_ADDR          0x1FFCC | ||||
| 
 | ||||
| #define HW_ACCESS_REGISTER_SIZE             4 | ||||
| 
 | ||||
| #define HW_ACCESS_PRAM_MAX_RANGE		0x3c000 | ||||
| 
 | ||||
| #define WSPI_CMD_READ                 0x40000000 | ||||
| #define WSPI_CMD_WRITE                0x00000000 | ||||
| #define WSPI_CMD_FIXED                0x20000000 | ||||
| #define WSPI_CMD_BYTE_LENGTH          0x1FFE0000 | ||||
| #define WSPI_CMD_BYTE_LENGTH_OFFSET   17 | ||||
| #define WSPI_CMD_BYTE_ADDR            0x0001FFFF | ||||
| 
 | ||||
| #define WSPI_INIT_CMD_CRC_LEN       5 | ||||
| 
 | ||||
| #define WSPI_INIT_CMD_START         0x00 | ||||
| #define WSPI_INIT_CMD_TX            0x40 | ||||
| /* the extra bypass bit is sampled by the TNET as '1' */ | ||||
| #define WSPI_INIT_CMD_BYPASS_BIT    0x80 | ||||
| #define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07 | ||||
| #define WSPI_INIT_CMD_EN_FIXEDBUSY  0x80 | ||||
| #define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00 | ||||
| #define WSPI_INIT_CMD_IOD           0x40 | ||||
| #define WSPI_INIT_CMD_IP            0x20 | ||||
| #define WSPI_INIT_CMD_CS            0x10 | ||||
| #define WSPI_INIT_CMD_WS            0x08 | ||||
| #define WSPI_INIT_CMD_WSPI          0x01 | ||||
| #define WSPI_INIT_CMD_END           0x01 | ||||
| 
 | ||||
| #define WSPI_INIT_CMD_LEN           8 | ||||
| 
 | ||||
| #define TNETWIF_READ_OFFSET_BYTES  8 | ||||
| #define HW_ACCESS_WSPI_FIXED_BUSY_LEN \ | ||||
| 		((TNETWIF_READ_OFFSET_BYTES - 4) / sizeof(u32)) | ||||
| #define HW_ACCESS_WSPI_INIT_CMD_MASK  0 | ||||
| 
 | ||||
| 
 | ||||
| /* Raw target IO, address is not translated */ | ||||
| void wl12xx_spi_read(struct wl12xx *wl, int addr, void *buf, size_t len); | ||||
| void wl12xx_spi_write(struct wl12xx *wl, int addr, void *buf, size_t len); | ||||
| 
 | ||||
| /* Memory target IO, address is tranlated to partition 0 */ | ||||
| void wl12xx_spi_mem_read(struct wl12xx *wl, int addr, void *buf, size_t len); | ||||
| void wl12xx_spi_mem_write(struct wl12xx *wl, int addr, void *buf, size_t len); | ||||
| u32 wl12xx_mem_read32(struct wl12xx *wl, int addr); | ||||
| void wl12xx_mem_write32(struct wl12xx *wl, int addr, u32 val); | ||||
| 
 | ||||
| /* Registers IO */ | ||||
| u32 wl12xx_reg_read32(struct wl12xx *wl, int addr); | ||||
| void wl12xx_reg_write32(struct wl12xx *wl, int addr, u32 val); | ||||
| 
 | ||||
| /* INIT and RESET words */ | ||||
| void wl12xx_spi_reset(struct wl12xx *wl); | ||||
| void wl12xx_spi_init(struct wl12xx *wl); | ||||
| void wl12xx_set_partition(struct wl12xx *wl, | ||||
| 			  u32 part_start, u32 part_size, | ||||
| 			  u32 reg_start,  u32 reg_size); | ||||
| 
 | ||||
| static inline u32 wl12xx_read32(struct wl12xx *wl, int addr) | ||||
| { | ||||
| 	u32 response; | ||||
| 
 | ||||
| 	wl12xx_spi_read(wl, addr, &response, sizeof(u32)); | ||||
| 
 | ||||
| 	return response; | ||||
| } | ||||
| 
 | ||||
| static inline void wl12xx_write32(struct wl12xx *wl, int addr, u32 val) | ||||
| { | ||||
| 	wl12xx_spi_write(wl, addr, &val, sizeof(u32)); | ||||
| } | ||||
| 
 | ||||
| #endif /* __WL12XX_SPI_H__ */ | ||||
							
								
								
									
										557
									
								
								drivers/net/wireless/wl12xx/tx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										557
									
								
								drivers/net/wireless/wl12xx/tx.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,557 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "tx.h" | ||||
| #include "ps.h" | ||||
| 
 | ||||
| static bool wl12xx_tx_double_buffer_busy(struct wl12xx *wl, u32 data_out_count) | ||||
| { | ||||
| 	int used, data_in_count; | ||||
| 
 | ||||
| 	data_in_count = wl->data_in_count; | ||||
| 
 | ||||
| 	if (data_in_count < data_out_count) | ||||
| 		/* data_in_count has wrapped */ | ||||
| 		data_in_count += TX_STATUS_DATA_OUT_COUNT_MASK + 1; | ||||
| 
 | ||||
| 	used = data_in_count - data_out_count; | ||||
| 
 | ||||
| 	WARN_ON(used < 0); | ||||
| 	WARN_ON(used > DP_TX_PACKET_RING_CHUNK_NUM); | ||||
| 
 | ||||
| 	if (used >= DP_TX_PACKET_RING_CHUNK_NUM) | ||||
| 		return true; | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_tx_path_status(struct wl12xx *wl) | ||||
| { | ||||
| 	u32 status, addr, data_out_count; | ||||
| 	bool busy; | ||||
| 
 | ||||
| 	addr = wl->data_path->tx_control_addr; | ||||
| 	status = wl12xx_mem_read32(wl, addr); | ||||
| 	data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK; | ||||
| 	busy = wl12xx_tx_double_buffer_busy(wl, data_out_count); | ||||
| 
 | ||||
| 	if (busy) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_tx_id(struct wl12xx *wl, struct sk_buff *skb) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | ||||
| 		if (wl->tx_frames[i] == NULL) { | ||||
| 			wl->tx_frames[i] = skb; | ||||
| 			return i; | ||||
| 		} | ||||
| 
 | ||||
| 	return -EBUSY; | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_tx_control(struct tx_double_buffer_desc *tx_hdr, | ||||
| 			      struct ieee80211_tx_info *control, u16 fc) | ||||
| { | ||||
| 	*(u16 *)&tx_hdr->control = 0; | ||||
| 
 | ||||
| 	tx_hdr->control.rate_policy = 0; | ||||
| 
 | ||||
| 	/* 802.11 packets */ | ||||
| 	tx_hdr->control.packet_type = 0; | ||||
| 
 | ||||
| 	if (control->flags & IEEE80211_TX_CTL_NO_ACK) | ||||
| 		tx_hdr->control.ack_policy = 1; | ||||
| 
 | ||||
| 	tx_hdr->control.tx_complete = 1; | ||||
| 
 | ||||
| 	if ((fc & IEEE80211_FTYPE_DATA) && | ||||
| 	    ((fc & IEEE80211_STYPE_QOS_DATA) || | ||||
| 	     (fc & IEEE80211_STYPE_QOS_NULLFUNC))) | ||||
| 		tx_hdr->control.qos = 1; | ||||
| } | ||||
| 
 | ||||
| /* RSN + MIC = 8 + 8 = 16 bytes (worst case - AES). */ | ||||
| #define MAX_MSDU_SECURITY_LENGTH      16 | ||||
| #define MAX_MPDU_SECURITY_LENGTH      16 | ||||
| #define WLAN_QOS_HDR_LEN              26 | ||||
| #define MAX_MPDU_HEADER_AND_SECURITY  (MAX_MPDU_SECURITY_LENGTH + \ | ||||
| 				       WLAN_QOS_HDR_LEN) | ||||
| #define HW_BLOCK_SIZE                 252 | ||||
| static void wl12xx_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr) | ||||
| { | ||||
| 	u16 payload_len, frag_threshold, mem_blocks; | ||||
| 	u16 num_mpdus, mem_blocks_per_frag; | ||||
| 
 | ||||
| 	frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; | ||||
| 	tx_hdr->frag_threshold = cpu_to_le16(frag_threshold); | ||||
| 
 | ||||
| 	payload_len = tx_hdr->length + MAX_MSDU_SECURITY_LENGTH; | ||||
| 
 | ||||
| 	if (payload_len > frag_threshold) { | ||||
| 		mem_blocks_per_frag = | ||||
| 			((frag_threshold + MAX_MPDU_HEADER_AND_SECURITY) / | ||||
| 			 HW_BLOCK_SIZE) + 1; | ||||
| 		num_mpdus = payload_len / frag_threshold; | ||||
| 		mem_blocks = num_mpdus * mem_blocks_per_frag; | ||||
| 		payload_len -= num_mpdus * frag_threshold; | ||||
| 		num_mpdus++; | ||||
| 
 | ||||
| 	} else { | ||||
| 		mem_blocks_per_frag = 0; | ||||
| 		mem_blocks = 0; | ||||
| 		num_mpdus = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	mem_blocks += (payload_len / HW_BLOCK_SIZE) + 1; | ||||
| 
 | ||||
| 	if (num_mpdus > 1) | ||||
| 		mem_blocks += min(num_mpdus, mem_blocks_per_frag); | ||||
| 
 | ||||
| 	tx_hdr->num_mem_blocks = mem_blocks; | ||||
| } | ||||
| 
 | ||||
| static int wl12xx_tx_fill_hdr(struct wl12xx *wl, struct sk_buff *skb, | ||||
| 			      struct ieee80211_tx_info *control) | ||||
| { | ||||
| 	struct tx_double_buffer_desc *tx_hdr; | ||||
| 	struct ieee80211_rate *rate; | ||||
| 	int id; | ||||
| 	u16 fc; | ||||
| 
 | ||||
| 	if (!skb) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	id = wl12xx_tx_id(wl, skb); | ||||
| 	if (id < 0) | ||||
| 		return id; | ||||
| 
 | ||||
| 	fc = *(u16 *)skb->data; | ||||
| 	tx_hdr = (struct tx_double_buffer_desc *) skb_push(skb, | ||||
| 							   sizeof(*tx_hdr)); | ||||
| 
 | ||||
| 	tx_hdr->length = cpu_to_le16(skb->len - sizeof(*tx_hdr)); | ||||
| 	rate = ieee80211_get_tx_rate(wl->hw, control); | ||||
| 	tx_hdr->rate = cpu_to_le16(rate->hw_value); | ||||
| 	tx_hdr->expiry_time = cpu_to_le32(1 << 16); | ||||
| 	tx_hdr->id = id; | ||||
| 
 | ||||
| 	/* FIXME: how to get the correct queue id? */ | ||||
| 	tx_hdr->xmit_queue = 0; | ||||
| 
 | ||||
| 	wl12xx_tx_control(tx_hdr, control, fc); | ||||
| 	wl12xx_tx_frag_block_num(tx_hdr); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* We copy the packet to the target */ | ||||
| static int wl12xx_tx_send_packet(struct wl12xx *wl, struct sk_buff *skb, | ||||
| 				 struct ieee80211_tx_info *control) | ||||
| { | ||||
| 	struct tx_double_buffer_desc *tx_hdr; | ||||
| 	int len; | ||||
| 	u32 addr; | ||||
| 
 | ||||
| 	if (!skb) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	tx_hdr = (struct tx_double_buffer_desc *) skb->data; | ||||
| 
 | ||||
| 	if (control->control.hw_key && | ||||
| 	    control->control.hw_key->alg == ALG_TKIP) { | ||||
| 		int hdrlen; | ||||
| 		u16 fc; | ||||
| 		u8 *pos; | ||||
| 
 | ||||
| 		fc = *(u16 *)(skb->data + sizeof(*tx_hdr)); | ||||
| 		tx_hdr->length += WL12XX_TKIP_IV_SPACE; | ||||
| 
 | ||||
| 		hdrlen = ieee80211_hdrlen(fc); | ||||
| 
 | ||||
| 		pos = skb_push(skb, WL12XX_TKIP_IV_SPACE); | ||||
| 		memmove(pos, pos + WL12XX_TKIP_IV_SPACE, | ||||
| 			sizeof(*tx_hdr) + hdrlen); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Revisit. This is a workaround for getting non-aligned packets.
 | ||||
| 	   This happens at least with EAPOL packets from the user space. | ||||
| 	   Our DMA requires packets to be aligned on a 4-byte boundary. | ||||
| 	*/ | ||||
| 	if (unlikely((long)skb->data & 0x03)) { | ||||
| 		int offset = (4 - (long)skb->data) & 0x03; | ||||
| 		wl12xx_debug(DEBUG_TX, "skb offset %d", offset); | ||||
| 
 | ||||
| 		/* check whether the current skb can be used */ | ||||
| 		if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { | ||||
| 			unsigned char *src = skb->data; | ||||
| 
 | ||||
| 			/* align the buffer on a 4-byte boundary */ | ||||
| 			skb_reserve(skb, offset); | ||||
| 			memmove(skb->data, src, skb->len); | ||||
| 		} else { | ||||
| 			wl12xx_info("No handler, fixme!"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Our skb->data at this point includes the HW header */ | ||||
| 	len = WL12XX_TX_ALIGN(skb->len); | ||||
| 
 | ||||
| 	if (wl->data_in_count & 0x1) | ||||
| 		addr = wl->data_path->tx_packet_ring_addr + | ||||
| 			wl->data_path->tx_packet_ring_chunk_size; | ||||
| 	else | ||||
| 		addr = wl->data_path->tx_packet_ring_addr; | ||||
| 
 | ||||
| 	wl12xx_spi_mem_write(wl, addr, skb->data, len); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x", | ||||
| 		     tx_hdr->id, skb, tx_hdr->length, tx_hdr->rate); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_tx_trigger(struct wl12xx *wl) | ||||
| { | ||||
| 	u32 data, addr; | ||||
| 
 | ||||
| 	if (wl->data_in_count & 0x1) { | ||||
| 		addr = ACX_REG_INTERRUPT_TRIG_H; | ||||
| 		data = INTR_TRIG_TX_PROC1; | ||||
| 	} else { | ||||
| 		addr = ACX_REG_INTERRUPT_TRIG; | ||||
| 		data = INTR_TRIG_TX_PROC0; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, addr, data); | ||||
| 
 | ||||
| 	/* Bumping data in */ | ||||
| 	wl->data_in_count = (wl->data_in_count + 1) & | ||||
| 		TX_STATUS_DATA_OUT_COUNT_MASK; | ||||
| } | ||||
| 
 | ||||
| /* caller must hold wl->mutex */ | ||||
| static int wl12xx_tx_frame(struct wl12xx *wl, struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_tx_info *info; | ||||
| 	int ret = 0; | ||||
| 	u8 idx; | ||||
| 
 | ||||
| 	info = IEEE80211_SKB_CB(skb); | ||||
| 
 | ||||
| 	if (info->control.hw_key) { | ||||
| 		idx = info->control.hw_key->hw_key_idx; | ||||
| 		if (unlikely(wl->default_key != idx)) { | ||||
| 			ret = wl12xx_acx_default_key(wl, idx); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ret = wl12xx_tx_path_status(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_tx_fill_hdr(wl, skb, info); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_tx_send_packet(wl, skb, info); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	wl12xx_tx_trigger(wl); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void wl12xx_tx_work(struct work_struct *work) | ||||
| { | ||||
| 	struct wl12xx *wl = container_of(work, struct wl12xx, tx_work); | ||||
| 	struct sk_buff *skb; | ||||
| 	bool woken_up = false; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 
 | ||||
| 	if (unlikely(wl->state == WL12XX_STATE_OFF)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	while ((skb = skb_dequeue(&wl->tx_queue))) { | ||||
| 		if (!woken_up) { | ||||
| 			wl12xx_ps_elp_wakeup(wl); | ||||
| 			woken_up = true; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = wl12xx_tx_frame(wl, skb); | ||||
| 		if (ret == -EBUSY) { | ||||
| 			/* firmware buffer is full, stop queues */ | ||||
| 			wl12xx_debug(DEBUG_TX, "tx_work: fw buffer full, " | ||||
| 				     "stop queues"); | ||||
| 			ieee80211_stop_queues(wl->hw); | ||||
| 			wl->tx_queue_stopped = true; | ||||
| 			skb_queue_head(&wl->tx_queue, skb); | ||||
| 			goto out; | ||||
| 		} else if (ret < 0) { | ||||
| 			dev_kfree_skb(skb); | ||||
| 			goto out; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	if (woken_up) | ||||
| 		wl12xx_ps_elp_sleep(wl); | ||||
| 
 | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| } | ||||
| 
 | ||||
| static const char *wl12xx_tx_parse_status(u8 status) | ||||
| { | ||||
| 	/* 8 bit status field, one character per bit plus null */ | ||||
| 	static char buf[9]; | ||||
| 	int i = 0; | ||||
| 
 | ||||
| 	memset(buf, 0, sizeof(buf)); | ||||
| 
 | ||||
| 	if (status & TX_DMA_ERROR) | ||||
| 		buf[i++] = 'm'; | ||||
| 	if (status & TX_DISABLED) | ||||
| 		buf[i++] = 'd'; | ||||
| 	if (status & TX_RETRY_EXCEEDED) | ||||
| 		buf[i++] = 'r'; | ||||
| 	if (status & TX_TIMEOUT) | ||||
| 		buf[i++] = 't'; | ||||
| 	if (status & TX_KEY_NOT_FOUND) | ||||
| 		buf[i++] = 'k'; | ||||
| 	if (status & TX_ENCRYPT_FAIL) | ||||
| 		buf[i++] = 'e'; | ||||
| 	if (status & TX_UNAVAILABLE_PRIORITY) | ||||
| 		buf[i++] = 'p'; | ||||
| 
 | ||||
| 	/* bit 0 is unused apparently */ | ||||
| 
 | ||||
| 	return buf; | ||||
| } | ||||
| 
 | ||||
| static void wl12xx_tx_packet_cb(struct wl12xx *wl, | ||||
| 				struct tx_result *result) | ||||
| { | ||||
| 	struct ieee80211_tx_info *info; | ||||
| 	struct sk_buff *skb; | ||||
| 	int hdrlen, ret; | ||||
| 	u8 *frame; | ||||
| 
 | ||||
| 	skb = wl->tx_frames[result->id]; | ||||
| 	if (skb == NULL) { | ||||
| 		wl12xx_error("SKB for packet %d is NULL", result->id); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	info = IEEE80211_SKB_CB(skb); | ||||
| 
 | ||||
| 	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && | ||||
| 	    (result->status == TX_SUCCESS)) | ||||
| 		info->flags |= IEEE80211_TX_STAT_ACK; | ||||
| 
 | ||||
| 	info->status.rates[0].count = result->ack_failures + 1; | ||||
| 	wl->stats.retry_count += result->ack_failures; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We have to remove our private TX header before pushing | ||||
| 	 * the skb back to mac80211. | ||||
| 	 */ | ||||
| 	frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc)); | ||||
| 	if (info->control.hw_key && | ||||
| 	    info->control.hw_key->alg == ALG_TKIP) { | ||||
| 		hdrlen = ieee80211_get_hdrlen_from_skb(skb); | ||||
| 		memmove(frame + WL12XX_TKIP_IV_SPACE, frame, hdrlen); | ||||
| 		skb_pull(skb, WL12XX_TKIP_IV_SPACE); | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" | ||||
| 		     " status 0x%x (%s)", | ||||
| 		     result->id, skb, result->ack_failures, result->rate, | ||||
| 		     result->status, wl12xx_tx_parse_status(result->status)); | ||||
| 
 | ||||
| 
 | ||||
| 	ieee80211_tx_status(wl->hw, skb); | ||||
| 
 | ||||
| 	wl->tx_frames[result->id] = NULL; | ||||
| 
 | ||||
| 	if (wl->tx_queue_stopped) { | ||||
| 		wl12xx_debug(DEBUG_TX, "cb: queue was stopped"); | ||||
| 
 | ||||
| 		skb = skb_dequeue(&wl->tx_queue); | ||||
| 
 | ||||
| 		/* The skb can be NULL because tx_work might have been
 | ||||
| 		   scheduled before the queue was stopped making the | ||||
| 		   queue empty */ | ||||
| 
 | ||||
| 		if (skb) { | ||||
| 			ret = wl12xx_tx_frame(wl, skb); | ||||
| 			if (ret == -EBUSY) { | ||||
| 				/* firmware buffer is still full */ | ||||
| 				wl12xx_debug(DEBUG_TX, "cb: fw buffer " | ||||
| 					     "still full"); | ||||
| 				skb_queue_head(&wl->tx_queue, skb); | ||||
| 				return; | ||||
| 			} else if (ret < 0) { | ||||
| 				dev_kfree_skb(skb); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		wl12xx_debug(DEBUG_TX, "cb: waking queues"); | ||||
| 		ieee80211_wake_queues(wl->hw); | ||||
| 		wl->tx_queue_stopped = false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Called upon reception of a TX complete interrupt */ | ||||
| void wl12xx_tx_complete(struct wl12xx *wl) | ||||
| { | ||||
| 	int i, result_index, num_complete = 0; | ||||
| 	struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; | ||||
| 
 | ||||
| 	if (unlikely(wl->state != WL12XX_STATE_ON)) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* First we read the result */ | ||||
| 	wl12xx_spi_mem_read(wl, wl->data_path->tx_complete_addr, | ||||
| 			    result, sizeof(result)); | ||||
| 
 | ||||
| 	result_index = wl->next_tx_complete; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(result); i++) { | ||||
| 		result_ptr = &result[result_index]; | ||||
| 
 | ||||
| 		if (result_ptr->done_1 == 1 && | ||||
| 		    result_ptr->done_2 == 1) { | ||||
| 			wl12xx_tx_packet_cb(wl, result_ptr); | ||||
| 
 | ||||
| 			result_ptr->done_1 = 0; | ||||
| 			result_ptr->done_2 = 0; | ||||
| 
 | ||||
| 			result_index = (result_index + 1) & | ||||
| 				(FW_TX_CMPLT_BLOCK_SIZE - 1); | ||||
| 			num_complete++; | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Every completed frame needs to be acknowledged */ | ||||
| 	if (num_complete) { | ||||
| 		/*
 | ||||
| 		 * If we've wrapped, we have to clear | ||||
| 		 * the results in 2 steps. | ||||
| 		 */ | ||||
| 		if (result_index > wl->next_tx_complete) { | ||||
| 			/* Only 1 write is needed */ | ||||
| 			wl12xx_spi_mem_write(wl, | ||||
| 					     wl->data_path->tx_complete_addr + | ||||
| 					     (wl->next_tx_complete * | ||||
| 					      sizeof(struct tx_result)), | ||||
| 					     &result[wl->next_tx_complete], | ||||
| 					     num_complete * | ||||
| 					     sizeof(struct tx_result)); | ||||
| 
 | ||||
| 
 | ||||
| 		} else if (result_index < wl->next_tx_complete) { | ||||
| 			/* 2 writes are needed */ | ||||
| 			wl12xx_spi_mem_write(wl, | ||||
| 					     wl->data_path->tx_complete_addr + | ||||
| 					     (wl->next_tx_complete * | ||||
| 					      sizeof(struct tx_result)), | ||||
| 					     &result[wl->next_tx_complete], | ||||
| 					     (FW_TX_CMPLT_BLOCK_SIZE - | ||||
| 					      wl->next_tx_complete) * | ||||
| 					     sizeof(struct tx_result)); | ||||
| 
 | ||||
| 			wl12xx_spi_mem_write(wl, | ||||
| 					     wl->data_path->tx_complete_addr, | ||||
| 					     result, | ||||
| 					     (num_complete - | ||||
| 					      FW_TX_CMPLT_BLOCK_SIZE + | ||||
| 					      wl->next_tx_complete) * | ||||
| 					     sizeof(struct tx_result)); | ||||
| 
 | ||||
| 		} else { | ||||
| 			/* We have to write the whole array */ | ||||
| 			wl12xx_spi_mem_write(wl, | ||||
| 					     wl->data_path->tx_complete_addr, | ||||
| 					     result, | ||||
| 					     FW_TX_CMPLT_BLOCK_SIZE * | ||||
| 					     sizeof(struct tx_result)); | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	wl->next_tx_complete = result_index; | ||||
| } | ||||
| 
 | ||||
| /* caller must hold wl->mutex */ | ||||
| void wl12xx_tx_flush(struct wl12xx *wl) | ||||
| { | ||||
| 	int i; | ||||
| 	struct sk_buff *skb; | ||||
| 	struct ieee80211_tx_info *info; | ||||
| 
 | ||||
| 	/* TX failure */ | ||||
| /* 	control->flags = 0; FIXME */ | ||||
| 
 | ||||
| 	while ((skb = skb_dequeue(&wl->tx_queue))) { | ||||
| 		info = IEEE80211_SKB_CB(skb); | ||||
| 
 | ||||
| 		wl12xx_debug(DEBUG_TX, "flushing skb 0x%p", skb); | ||||
| 
 | ||||
| 		if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) | ||||
| 				continue; | ||||
| 
 | ||||
| 		ieee80211_tx_status(wl->hw, skb); | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) | ||||
| 		if (wl->tx_frames[i] != NULL) { | ||||
| 			skb = wl->tx_frames[i]; | ||||
| 			info = IEEE80211_SKB_CB(skb); | ||||
| 
 | ||||
| 			if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) | ||||
| 				continue; | ||||
| 
 | ||||
| 			ieee80211_tx_status(wl->hw, skb); | ||||
| 			wl->tx_frames[i] = NULL; | ||||
| 		} | ||||
| } | ||||
							
								
								
									
										215
									
								
								drivers/net/wireless/wl12xx/tx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								drivers/net/wireless/wl12xx/tx.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_TX_H__ | ||||
| #define __WL12XX_TX_H__ | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * | ||||
|  * TX PATH | ||||
|  * | ||||
|  * The Tx path uses a double buffer and a tx_control structure, each located | ||||
|  * at a fixed address in the device's memory. On startup, the host retrieves | ||||
|  * the pointers to these addresses. A double buffer allows for continuous data | ||||
|  * flow towards the device. The host keeps track of which buffer is available | ||||
|  * and alternates between these two buffers on a per packet basis. | ||||
|  * | ||||
|  * The size of each of the two buffers is large enough to hold the longest | ||||
|  * 802.3 packet - maximum size Ethernet packet + header + descriptor. | ||||
|  * TX complete indication will be received a-synchronously in a TX done cyclic | ||||
|  * buffer which is composed of 16 tx_result descriptors structures and is used | ||||
|  * in a cyclic manner. | ||||
|  * | ||||
|  * The TX (HOST) procedure is as follows: | ||||
|  * 1. Read the Tx path status, that will give the data_out_count. | ||||
|  * 2. goto 1, if not possible. | ||||
|  *    i.e. if data_in_count - data_out_count >= HwBuffer size (2 for double | ||||
|  *    buffer). | ||||
|  * 3. Copy the packet (preceded by double_buffer_desc), if possible. | ||||
|  *    i.e. if data_in_count - data_out_count < HwBuffer size (2 for double | ||||
|  *    buffer). | ||||
|  * 4. increment data_in_count. | ||||
|  * 5. Inform the firmware by generating a firmware internal interrupt. | ||||
|  * 6. FW will increment data_out_count after it reads the buffer. | ||||
|  * | ||||
|  * The TX Complete procedure: | ||||
|  * 1. To get a TX complete indication the host enables the tx_complete flag in | ||||
|  *    the TX descriptor Structure. | ||||
|  * 2. For each packet with a Tx Complete field set, the firmware adds the | ||||
|  *    transmit results to the cyclic buffer (txDoneRing) and sets both done_1 | ||||
|  *    and done_2 to 1 to indicate driver ownership. | ||||
|  * 3. The firmware sends a Tx Complete interrupt to the host to trigger the | ||||
|  *    host to process the new data. Note: interrupt will be send per packet if | ||||
|  *    TX complete indication was requested in tx_control or per crossing | ||||
|  *    aggregation threshold. | ||||
|  * 4. After receiving the Tx Complete interrupt, the host reads the | ||||
|  *    TxDescriptorDone information in a cyclic manner and clears both done_1 | ||||
|  *    and done_2 fields. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define TX_COMPLETE_REQUIRED_BIT	0x80 | ||||
| #define TX_STATUS_DATA_OUT_COUNT_MASK   0xf | ||||
| #define WL12XX_TX_ALIGN_TO 4 | ||||
| #define WL12XX_TX_ALIGN(len) (((len) + WL12XX_TX_ALIGN_TO - 1) & \ | ||||
| 			     ~(WL12XX_TX_ALIGN_TO - 1)) | ||||
| #define WL12XX_TKIP_IV_SPACE 4 | ||||
| 
 | ||||
| struct tx_control { | ||||
| 	/* Rate Policy (class) index */ | ||||
| 	unsigned rate_policy:3; | ||||
| 
 | ||||
| 	/* When set, no ack policy is expected */ | ||||
| 	unsigned ack_policy:1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Packet type: | ||||
| 	 * 0 -> 802.11 | ||||
| 	 * 1 -> 802.3 | ||||
| 	 * 2 -> IP | ||||
| 	 * 3 -> raw codec | ||||
| 	 */ | ||||
| 	unsigned packet_type:2; | ||||
| 
 | ||||
| 	/* If set, this is a QoS-Null or QoS-Data frame */ | ||||
| 	unsigned qos:1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If set, the target triggers the tx complete INT | ||||
| 	 * upon frame sending completion. | ||||
| 	 */ | ||||
| 	unsigned tx_complete:1; | ||||
| 
 | ||||
| 	/* 2 bytes padding before packet header */ | ||||
| 	unsigned xfer_pad:1; | ||||
| 
 | ||||
| 	unsigned reserved:7; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| struct tx_double_buffer_desc { | ||||
| 	/* Length of payload, including headers. */ | ||||
| 	u16 length; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * A bit mask that specifies the initial rate to be used | ||||
| 	 * Possible values are: | ||||
| 	 * 0x0001 - 1Mbits | ||||
| 	 * 0x0002 - 2Mbits | ||||
| 	 * 0x0004 - 5.5Mbits | ||||
| 	 * 0x0008 - 6Mbits | ||||
| 	 * 0x0010 - 9Mbits | ||||
| 	 * 0x0020 - 11Mbits | ||||
| 	 * 0x0040 - 12Mbits | ||||
| 	 * 0x0080 - 18Mbits | ||||
| 	 * 0x0100 - 22Mbits | ||||
| 	 * 0x0200 - 24Mbits | ||||
| 	 * 0x0400 - 36Mbits | ||||
| 	 * 0x0800 - 48Mbits | ||||
| 	 * 0x1000 - 54Mbits | ||||
| 	 */ | ||||
| 	u16 rate; | ||||
| 
 | ||||
| 	/* Time in us that a packet can spend in the target */ | ||||
| 	u32 expiry_time; | ||||
| 
 | ||||
| 	/* index of the TX queue used for this packet */ | ||||
| 	u8 xmit_queue; | ||||
| 
 | ||||
| 	/* Used to identify a packet */ | ||||
| 	u8 id; | ||||
| 
 | ||||
| 	struct tx_control control; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The FW should cut the packet into fragments | ||||
| 	 * of this size. | ||||
| 	 */ | ||||
| 	u16 frag_threshold; | ||||
| 
 | ||||
| 	/* Numbers of HW queue blocks to be allocated */ | ||||
| 	u8 num_mem_blocks; | ||||
| 
 | ||||
| 	u8 reserved; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| enum { | ||||
| 	TX_SUCCESS              = 0, | ||||
| 	TX_DMA_ERROR            = BIT(7), | ||||
| 	TX_DISABLED             = BIT(6), | ||||
| 	TX_RETRY_EXCEEDED       = BIT(5), | ||||
| 	TX_TIMEOUT              = BIT(4), | ||||
| 	TX_KEY_NOT_FOUND        = BIT(3), | ||||
| 	TX_ENCRYPT_FAIL         = BIT(2), | ||||
| 	TX_UNAVAILABLE_PRIORITY = BIT(1), | ||||
| }; | ||||
| 
 | ||||
| struct tx_result { | ||||
| 	/*
 | ||||
| 	 * Ownership synchronization between the host and | ||||
| 	 * the firmware. If done_1 and done_2 are cleared, | ||||
| 	 * owned by the FW (no info ready). | ||||
| 	 */ | ||||
| 	u8 done_1; | ||||
| 
 | ||||
| 	/* same as double_buffer_desc->id */ | ||||
| 	u8 id; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Total air access duration consumed by this | ||||
| 	 * packet, including all retries and overheads. | ||||
| 	 */ | ||||
| 	u16 medium_usage; | ||||
| 
 | ||||
| 	/* Total media delay (from 1st EDCA AIFS counter until TX Complete). */ | ||||
| 	u32 medium_delay; | ||||
| 
 | ||||
| 	/* Time between host xfer and tx complete */ | ||||
| 	u32 fw_hnadling_time; | ||||
| 
 | ||||
| 	/* The LS-byte of the last TKIP sequence number. */ | ||||
| 	u8 lsb_seq_num; | ||||
| 
 | ||||
| 	/* Retry count */ | ||||
| 	u8 ack_failures; | ||||
| 
 | ||||
| 	/* At which rate we got a ACK */ | ||||
| 	u16 rate; | ||||
| 
 | ||||
| 	u16 reserved; | ||||
| 
 | ||||
| 	/* TX_* */ | ||||
| 	u8 status; | ||||
| 
 | ||||
| 	/* See done_1 */ | ||||
| 	u8 done_2; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| void wl12xx_tx_work(struct work_struct *work); | ||||
| void wl12xx_tx_complete(struct wl12xx *wl); | ||||
| void wl12xx_tx_flush(struct wl12xx *wl); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										709
									
								
								drivers/net/wireless/wl12xx/wl1251.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										709
									
								
								drivers/net/wireless/wl12xx/wl1251.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,709 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2008-2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include "wl1251.h" | ||||
| #include "reg.h" | ||||
| #include "spi.h" | ||||
| #include "boot.h" | ||||
| #include "event.h" | ||||
| #include "acx.h" | ||||
| #include "tx.h" | ||||
| #include "rx.h" | ||||
| #include "ps.h" | ||||
| #include "init.h" | ||||
| 
 | ||||
| static struct wl12xx_partition_set wl1251_part_table[PART_TABLE_LEN] = { | ||||
| 	[PART_DOWN] = { | ||||
| 		.mem = { | ||||
| 			.start = 0x00000000, | ||||
| 			.size  = 0x00016800 | ||||
| 		}, | ||||
| 		.reg = { | ||||
| 			.start = REGISTERS_BASE, | ||||
| 			.size  = REGISTERS_DOWN_SIZE | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	[PART_WORK] = { | ||||
| 		.mem = { | ||||
| 			.start = 0x00028000, | ||||
| 			.size  = 0x00014000 | ||||
| 		}, | ||||
| 		.reg = { | ||||
| 			.start = REGISTERS_BASE, | ||||
| 			.size  = REGISTERS_WORK_SIZE | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	/* WL1251 doesn't use the DRPW partition, so we don't set it here */ | ||||
| }; | ||||
| 
 | ||||
| static enum wl12xx_acx_int_reg wl1251_acx_reg_table[ACX_REG_TABLE_LEN] = { | ||||
| 	[ACX_REG_INTERRUPT_TRIG]     = (REGISTERS_BASE + 0x0474), | ||||
| 	[ACX_REG_INTERRUPT_TRIG_H]   = (REGISTERS_BASE + 0x0478), | ||||
| 	[ACX_REG_INTERRUPT_MASK]     = (REGISTERS_BASE + 0x0494), | ||||
| 	[ACX_REG_HINT_MASK_SET]      = (REGISTERS_BASE + 0x0498), | ||||
| 	[ACX_REG_HINT_MASK_CLR]      = (REGISTERS_BASE + 0x049C), | ||||
| 	[ACX_REG_INTERRUPT_NO_CLEAR] = (REGISTERS_BASE + 0x04B0), | ||||
| 	[ACX_REG_INTERRUPT_CLEAR]    = (REGISTERS_BASE + 0x04A4), | ||||
| 	[ACX_REG_INTERRUPT_ACK]      = (REGISTERS_BASE + 0x04A8), | ||||
| 	[ACX_REG_SLV_SOFT_RESET]     = (REGISTERS_BASE + 0x0000), | ||||
| 	[ACX_REG_EE_START]           = (REGISTERS_BASE + 0x080C), | ||||
| 	[ACX_REG_ECPU_CONTROL]       = (REGISTERS_BASE + 0x0804) | ||||
| }; | ||||
| 
 | ||||
| static int wl1251_upload_firmware(struct wl12xx *wl) | ||||
| { | ||||
| 	struct wl12xx_partition_set *p_table = wl->chip.p_table; | ||||
| 	int addr, chunk_num, partition_limit; | ||||
| 	size_t fw_data_len; | ||||
| 	u8 *p; | ||||
| 
 | ||||
| 	/* whal_FwCtrl_LoadFwImageSm() */ | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", | ||||
| 		     wl12xx_reg_read32(wl, CHIP_ID_B)); | ||||
| 
 | ||||
| 	/* 10.0 check firmware length and set partition */ | ||||
| 	fw_data_len =  (wl->fw[4] << 24) | (wl->fw[5] << 16) | | ||||
| 		(wl->fw[6] << 8) | (wl->fw[7]); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, | ||||
| 		CHUNK_SIZE); | ||||
| 
 | ||||
| 	if ((fw_data_len % 4) != 0) { | ||||
| 		wl12xx_error("firmware length not multiple of four"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	wl12xx_set_partition(wl, | ||||
| 			     p_table[PART_DOWN].mem.start, | ||||
| 			     p_table[PART_DOWN].mem.size, | ||||
| 			     p_table[PART_DOWN].reg.start, | ||||
| 			     p_table[PART_DOWN].reg.size); | ||||
| 
 | ||||
| 	/* 10.1 set partition limit and chunk num */ | ||||
| 	chunk_num = 0; | ||||
| 	partition_limit = p_table[PART_DOWN].mem.size; | ||||
| 
 | ||||
| 	while (chunk_num < fw_data_len / CHUNK_SIZE) { | ||||
| 		/* 10.2 update partition, if needed */ | ||||
| 		addr = p_table[PART_DOWN].mem.start + | ||||
| 			(chunk_num + 2) * CHUNK_SIZE; | ||||
| 		if (addr > partition_limit) { | ||||
| 			addr = p_table[PART_DOWN].mem.start + | ||||
| 				chunk_num * CHUNK_SIZE; | ||||
| 			partition_limit = chunk_num * CHUNK_SIZE + | ||||
| 				p_table[PART_DOWN].mem.size; | ||||
| 			wl12xx_set_partition(wl, | ||||
| 					     addr, | ||||
| 					     p_table[PART_DOWN].mem.size, | ||||
| 					     p_table[PART_DOWN].reg.start, | ||||
| 					     p_table[PART_DOWN].reg.size); | ||||
| 		} | ||||
| 
 | ||||
| 		/* 10.3 upload the chunk */ | ||||
| 		addr = p_table[PART_DOWN].mem.start + chunk_num * CHUNK_SIZE; | ||||
| 		p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; | ||||
| 		wl12xx_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", | ||||
| 			     p, addr); | ||||
| 		wl12xx_spi_mem_write(wl, addr, p, CHUNK_SIZE); | ||||
| 
 | ||||
| 		chunk_num++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* 10.4 upload the last chunk */ | ||||
| 	addr = p_table[PART_DOWN].mem.start + chunk_num * CHUNK_SIZE; | ||||
| 	p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; | ||||
| 	wl12xx_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", | ||||
| 		     fw_data_len % CHUNK_SIZE, p, addr); | ||||
| 	wl12xx_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_upload_nvs(struct wl12xx *wl) | ||||
| { | ||||
| 	size_t nvs_len, nvs_bytes_written, burst_len; | ||||
| 	int nvs_start, i; | ||||
| 	u32 dest_addr, val; | ||||
| 	u8 *nvs_ptr, *nvs; | ||||
| 
 | ||||
| 	nvs = wl->nvs; | ||||
| 	if (nvs == NULL) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	nvs_ptr = nvs; | ||||
| 
 | ||||
| 	nvs_len = wl->nvs_len; | ||||
| 	nvs_start = wl->fw_len; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Layout before the actual NVS tables: | ||||
| 	 * 1 byte : burst length. | ||||
| 	 * 2 bytes: destination address. | ||||
| 	 * n bytes: data to burst copy. | ||||
| 	 * | ||||
| 	 * This is ended by a 0 length, then the NVS tables. | ||||
| 	 */ | ||||
| 
 | ||||
| 	while (nvs_ptr[0]) { | ||||
| 		burst_len = nvs_ptr[0]; | ||||
| 		dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); | ||||
| 
 | ||||
| 		/* We move our pointer to the data */ | ||||
| 		nvs_ptr += 3; | ||||
| 
 | ||||
| 		for (i = 0; i < burst_len; i++) { | ||||
| 			val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | ||||
| 			       | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); | ||||
| 
 | ||||
| 			wl12xx_debug(DEBUG_BOOT, | ||||
| 				     "nvs burst write 0x%x: 0x%x", | ||||
| 				     dest_addr, val); | ||||
| 			wl12xx_mem_write32(wl, dest_addr, val); | ||||
| 
 | ||||
| 			nvs_ptr += 4; | ||||
| 			dest_addr += 4; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We've reached the first zero length, the first NVS table | ||||
| 	 * is 7 bytes further. | ||||
| 	 */ | ||||
| 	nvs_ptr += 7; | ||||
| 	nvs_len -= nvs_ptr - nvs; | ||||
| 	nvs_len = ALIGN(nvs_len, 4); | ||||
| 
 | ||||
| 	/* Now we must set the partition correctly */ | ||||
| 	wl12xx_set_partition(wl, nvs_start, | ||||
| 			     wl->chip.p_table[PART_DOWN].mem.size, | ||||
| 			     wl->chip.p_table[PART_DOWN].reg.start, | ||||
| 			     wl->chip.p_table[PART_DOWN].reg.size); | ||||
| 
 | ||||
| 	/* And finally we upload the NVS tables */ | ||||
| 	nvs_bytes_written = 0; | ||||
| 	while (nvs_bytes_written < nvs_len) { | ||||
| 		val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | ||||
| 		       | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); | ||||
| 
 | ||||
| 		val = cpu_to_le32(val); | ||||
| 
 | ||||
| 		wl12xx_debug(DEBUG_BOOT, | ||||
| 			     "nvs write table 0x%x: 0x%x", | ||||
| 			     nvs_start, val); | ||||
| 		wl12xx_mem_write32(wl, nvs_start, val); | ||||
| 
 | ||||
| 		nvs_ptr += 4; | ||||
| 		nvs_bytes_written += 4; | ||||
| 		nvs_start += 4; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_boot(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret = 0, minor_minor_e2_ver; | ||||
| 	u32 tmp, boot_data; | ||||
| 
 | ||||
| 	ret = wl12xx_boot_soft_reset(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* 2. start processing NVS file */ | ||||
| 	ret = wl->chip.op_upload_nvs(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* write firmware's last address (ie. it's length) to
 | ||||
| 	 * ACX_EEPROMLESS_IND_REG */ | ||||
| 	wl12xx_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); | ||||
| 
 | ||||
| 	/* 6. read the EEPROM parameters */ | ||||
| 	tmp = wl12xx_reg_read32(wl, SCR_PAD2); | ||||
| 
 | ||||
| 	/* 7. read bootdata */ | ||||
| 	wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8; | ||||
| 	wl->boot_attr.major = (tmp & 0x00FF0000) >> 16; | ||||
| 	tmp = wl12xx_reg_read32(wl, SCR_PAD3); | ||||
| 
 | ||||
| 	/* 8. check bootdata and call restart sequence */ | ||||
| 	wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16; | ||||
| 	minor_minor_e2_ver = (tmp & 0xFF000000) >> 24; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x " | ||||
| 		     "minorE2Ver 0x%x minor_minor_e2_ver 0x%x", | ||||
| 		     wl->boot_attr.radio_type, wl->boot_attr.major, | ||||
| 		     wl->boot_attr.minor, minor_minor_e2_ver); | ||||
| 
 | ||||
| 	ret = wl12xx_boot_init_seq(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* 9. NVS processing done */ | ||||
| 	boot_data = wl12xx_reg_read32(wl, ACX_REG_ECPU_CONTROL); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data); | ||||
| 
 | ||||
| 	/* 10. check that ECPU_CONTROL_HALT bits are set in
 | ||||
| 	 * pWhalBus->uBootData and start uploading firmware | ||||
| 	 */ | ||||
| 	if ((boot_data & ECPU_CONTROL_HALT) == 0) { | ||||
| 		wl12xx_error("boot failed, ECPU_CONTROL_HALT not set"); | ||||
| 		ret = -EIO; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = wl->chip.op_upload_fw(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* 10.5 start firmware */ | ||||
| 	ret = wl12xx_boot_run_firmware(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* Get and save the firmware version */ | ||||
| 	wl12xx_acx_fw_version(wl, wl->chip.fw_ver, sizeof(wl->chip.fw_ver)); | ||||
| 
 | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_mem_cfg(struct wl12xx *wl) | ||||
| { | ||||
| 	struct wl1251_acx_config_memory mem_conf; | ||||
| 	int ret, i; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "wl1251 mem cfg"); | ||||
| 
 | ||||
| 	/* memory config */ | ||||
| 	mem_conf.mem_config.num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); | ||||
| 	mem_conf.mem_config.rx_mem_block_num = 35; | ||||
| 	mem_conf.mem_config.tx_min_mem_block_num = 64; | ||||
| 	mem_conf.mem_config.num_tx_queues = MAX_TX_QUEUES; | ||||
| 	mem_conf.mem_config.host_if_options = HOSTIF_PKT_RING; | ||||
| 	mem_conf.mem_config.num_ssid_profiles = 1; | ||||
| 	mem_conf.mem_config.debug_buffer_size = | ||||
| 		cpu_to_le16(TRACE_BUFFER_MAX_SIZE); | ||||
| 
 | ||||
| 	/* RX queue config */ | ||||
| 	mem_conf.rx_queue_config.dma_address = 0; | ||||
| 	mem_conf.rx_queue_config.num_descs = ACX_RX_DESC_DEF; | ||||
| 	mem_conf.rx_queue_config.priority = DEFAULT_RXQ_PRIORITY; | ||||
| 	mem_conf.rx_queue_config.type = DEFAULT_RXQ_TYPE; | ||||
| 
 | ||||
| 	/* TX queue config */ | ||||
| 	for (i = 0; i < MAX_TX_QUEUES; i++) { | ||||
| 		mem_conf.tx_queue_config[i].num_descs = ACX_TX_DESC_DEF; | ||||
| 		mem_conf.tx_queue_config[i].attributes = i; | ||||
| 	} | ||||
| 
 | ||||
| 	mem_conf.header.id = ACX_MEM_CFG; | ||||
| 	mem_conf.header.len = sizeof(struct wl1251_acx_config_memory) - | ||||
| 		sizeof(struct acx_header); | ||||
| 	mem_conf.header.len -= | ||||
| 		(MAX_TX_QUEUE_CONFIGS - mem_conf.mem_config.num_tx_queues) * | ||||
| 		sizeof(struct wl1251_acx_tx_queue_config); | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_configure(wl, &mem_conf, | ||||
| 				   sizeof(struct wl1251_acx_config_memory)); | ||||
| 	if (ret < 0) | ||||
| 		wl12xx_warning("wl1251 mem config failed: %d", ret); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_hw_init_mem_config(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl1251_mem_cfg(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map), | ||||
| 					  GFP_KERNEL); | ||||
| 	if (!wl->target_mem_map) { | ||||
| 		wl12xx_error("couldn't allocate target memory map"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	/* we now ask for the firmware built memory map */ | ||||
| 	ret = wl12xx_acx_mem_map(wl, wl->target_mem_map, | ||||
| 				 sizeof(struct wl1251_acx_mem_map)); | ||||
| 	if (ret < 0) { | ||||
| 		wl12xx_error("couldn't retrieve firmware memory map"); | ||||
| 		kfree(wl->target_mem_map); | ||||
| 		wl->target_mem_map = NULL; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void wl1251_set_ecpu_ctrl(struct wl12xx *wl, u32 flag) | ||||
| { | ||||
| 	u32 cpu_ctrl; | ||||
| 
 | ||||
| 	/* 10.5.0 run the firmware (I) */ | ||||
| 	cpu_ctrl = wl12xx_reg_read32(wl, ACX_REG_ECPU_CONTROL); | ||||
| 
 | ||||
| 	/* 10.5.1 run the firmware (II) */ | ||||
| 	cpu_ctrl &= ~flag; | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); | ||||
| } | ||||
| 
 | ||||
| static void wl1251_target_enable_interrupts(struct wl12xx *wl) | ||||
| { | ||||
| 	/* Enable target's interrupts */ | ||||
| 	wl->intr_mask = WL1251_ACX_INTR_RX0_DATA | | ||||
| 		WL1251_ACX_INTR_RX1_DATA | | ||||
| 		WL1251_ACX_INTR_TX_RESULT | | ||||
| 		WL1251_ACX_INTR_EVENT_A | | ||||
| 		WL1251_ACX_INTR_EVENT_B | | ||||
| 		WL1251_ACX_INTR_INIT_COMPLETE; | ||||
| 	wl12xx_boot_target_enable_interrupts(wl); | ||||
| } | ||||
| 
 | ||||
| static void wl1251_irq_work(struct work_struct *work) | ||||
| { | ||||
| 	u32 intr; | ||||
| 	struct wl12xx *wl = | ||||
| 		container_of(work, struct wl12xx, irq_work); | ||||
| 
 | ||||
| 	mutex_lock(&wl->mutex); | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_IRQ, "IRQ work"); | ||||
| 
 | ||||
| 	if (wl->state == WL12XX_STATE_OFF) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	wl12xx_ps_elp_wakeup(wl); | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1251_ACX_INTR_ALL); | ||||
| 
 | ||||
| 	intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); | ||||
| 	wl12xx_debug(DEBUG_IRQ, "intr: 0x%x", intr); | ||||
| 
 | ||||
| 	if (wl->data_path) { | ||||
| 		wl12xx_spi_mem_read(wl, wl->data_path->rx_control_addr, | ||||
| 				    &wl->rx_counter, sizeof(u32)); | ||||
| 
 | ||||
| 		/* We handle a frmware bug here */ | ||||
| 		switch ((wl->rx_counter - wl->rx_handled) & 0xf) { | ||||
| 		case 0: | ||||
| 			wl12xx_debug(DEBUG_IRQ, "RX: FW and host in sync"); | ||||
| 			intr &= ~WL1251_ACX_INTR_RX0_DATA; | ||||
| 			intr &= ~WL1251_ACX_INTR_RX1_DATA; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			wl12xx_debug(DEBUG_IRQ, "RX: FW +1"); | ||||
| 			intr |= WL1251_ACX_INTR_RX0_DATA; | ||||
| 			intr &= ~WL1251_ACX_INTR_RX1_DATA; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			wl12xx_debug(DEBUG_IRQ, "RX: FW +2"); | ||||
| 			intr |= WL1251_ACX_INTR_RX0_DATA; | ||||
| 			intr |= WL1251_ACX_INTR_RX1_DATA; | ||||
| 			break; | ||||
| 		default: | ||||
| 			wl12xx_warning("RX: FW and host out of sync: %d", | ||||
| 				       wl->rx_counter - wl->rx_handled); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		wl->rx_handled = wl->rx_counter; | ||||
| 
 | ||||
| 
 | ||||
| 		wl12xx_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); | ||||
| 	} | ||||
| 
 | ||||
| 	intr &= wl->intr_mask; | ||||
| 
 | ||||
| 	if (intr == 0) { | ||||
| 		wl12xx_debug(DEBUG_IRQ, "INTR is 0"); | ||||
| 		wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, | ||||
| 				   ~(wl->intr_mask)); | ||||
| 
 | ||||
| 		goto out_sleep; | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_RX0_DATA) { | ||||
| 		wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); | ||||
| 		wl12xx_rx(wl); | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_RX1_DATA) { | ||||
| 		wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); | ||||
| 		wl12xx_rx(wl); | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_TX_RESULT) { | ||||
| 		wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); | ||||
| 		wl12xx_tx_complete(wl); | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { | ||||
| 		wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); | ||||
| 		if (intr & WL1251_ACX_INTR_EVENT_A) | ||||
| 			wl12xx_event_handle(wl, 0); | ||||
| 		else | ||||
| 			wl12xx_event_handle(wl, 1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (intr & WL1251_ACX_INTR_INIT_COMPLETE) | ||||
| 		wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); | ||||
| 
 | ||||
| 	wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); | ||||
| 
 | ||||
| out_sleep: | ||||
| 	wl12xx_ps_elp_sleep(wl); | ||||
| out: | ||||
| 	mutex_unlock(&wl->mutex); | ||||
| } | ||||
| 
 | ||||
| static int wl1251_hw_init_txq_fill(u8 qid, | ||||
| 				   struct acx_tx_queue_qos_config *config, | ||||
| 				   u32 num_blocks) | ||||
| { | ||||
| 	config->qid = qid; | ||||
| 
 | ||||
| 	switch (qid) { | ||||
| 	case QOS_AC_BE: | ||||
| 		config->high_threshold = | ||||
| 			(QOS_TX_HIGH_BE_DEF * num_blocks) / 100; | ||||
| 		config->low_threshold = | ||||
| 			(QOS_TX_LOW_BE_DEF * num_blocks) / 100; | ||||
| 		break; | ||||
| 	case QOS_AC_BK: | ||||
| 		config->high_threshold = | ||||
| 			(QOS_TX_HIGH_BK_DEF * num_blocks) / 100; | ||||
| 		config->low_threshold = | ||||
| 			(QOS_TX_LOW_BK_DEF * num_blocks) / 100; | ||||
| 		break; | ||||
| 	case QOS_AC_VI: | ||||
| 		config->high_threshold = | ||||
| 			(QOS_TX_HIGH_VI_DEF * num_blocks) / 100; | ||||
| 		config->low_threshold = | ||||
| 			(QOS_TX_LOW_VI_DEF * num_blocks) / 100; | ||||
| 		break; | ||||
| 	case QOS_AC_VO: | ||||
| 		config->high_threshold = | ||||
| 			(QOS_TX_HIGH_VO_DEF * num_blocks) / 100; | ||||
| 		config->low_threshold = | ||||
| 			(QOS_TX_LOW_VO_DEF * num_blocks) / 100; | ||||
| 		break; | ||||
| 	default: | ||||
| 		wl12xx_error("Invalid TX queue id: %d", qid); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_hw_init_tx_queue_config(struct wl12xx *wl) | ||||
| { | ||||
| 	struct acx_tx_queue_qos_config config; | ||||
| 	struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map; | ||||
| 	int ret, i; | ||||
| 
 | ||||
| 	wl12xx_debug(DEBUG_ACX, "acx tx queue config"); | ||||
| 
 | ||||
| 	config.header.id = ACX_TX_QUEUE_CFG; | ||||
| 	config.header.len = sizeof(struct acx_tx_queue_qos_config) - | ||||
| 		sizeof(struct acx_header); | ||||
| 
 | ||||
| 	for (i = 0; i < MAX_NUM_OF_AC; i++) { | ||||
| 		ret = wl1251_hw_init_txq_fill(i, &config, | ||||
| 					      wl_mem_map->num_tx_mem_blocks); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		ret = wl12xx_cmd_configure(wl, &config, sizeof(config)); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_hw_init_data_path_config(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* asking for the data path parameters */ | ||||
| 	wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp), | ||||
| 				GFP_KERNEL); | ||||
| 	if (!wl->data_path) { | ||||
| 		wl12xx_error("Couldnt allocate data path parameters"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = wl12xx_acx_data_path_params(wl, wl->data_path); | ||||
| 	if (ret < 0) { | ||||
| 		kfree(wl->data_path); | ||||
| 		wl->data_path = NULL; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_hw_init(struct wl12xx *wl) | ||||
| { | ||||
| 	struct wl1251_acx_mem_map *wl_mem_map; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl12xx_hw_init_hwenc_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Template settings */ | ||||
| 	ret = wl12xx_hw_init_templates_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Default memory configuration */ | ||||
| 	ret = wl1251_hw_init_mem_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Default data path configuration  */ | ||||
| 	ret = wl1251_hw_init_data_path_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_memmap; | ||||
| 
 | ||||
| 	/* RX config */ | ||||
| 	ret = wl12xx_hw_init_rx_config(wl, | ||||
| 				       RX_CFG_PROMISCUOUS | RX_CFG_TSF, | ||||
| 				       RX_FILTER_OPTION_DEF); | ||||
| 	/* RX_CONFIG_OPTION_ANY_DST_ANY_BSS,
 | ||||
| 	   RX_FILTER_OPTION_FILTER_ALL); */ | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* TX queues config */ | ||||
| 	ret = wl1251_hw_init_tx_queue_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* PHY layer config */ | ||||
| 	ret = wl12xx_hw_init_phy_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Beacon filtering */ | ||||
| 	ret = wl12xx_hw_init_beacon_filter(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Bluetooth WLAN coexistence */ | ||||
| 	ret = wl12xx_hw_init_pta(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Energy detection */ | ||||
| 	ret = wl12xx_hw_init_energy_detection(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Beacons and boradcast settings */ | ||||
| 	ret = wl12xx_hw_init_beacon_broadcast(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Enable data path */ | ||||
| 	ret = wl12xx_cmd_data_path(wl, wl->channel, 1); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	/* Default power state */ | ||||
| 	ret = wl12xx_hw_init_power_auth(wl); | ||||
| 	if (ret < 0) | ||||
| 		goto out_free_data_path; | ||||
| 
 | ||||
| 	wl_mem_map = wl->target_mem_map; | ||||
| 	wl12xx_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x", | ||||
| 		    wl_mem_map->num_tx_mem_blocks, | ||||
| 		    wl->data_path->tx_control_addr, | ||||
| 		    wl_mem_map->num_rx_mem_blocks, | ||||
| 		    wl->data_path->rx_control_addr); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  out_free_data_path: | ||||
| 	kfree(wl->data_path); | ||||
| 
 | ||||
|  out_free_memmap: | ||||
| 	kfree(wl->target_mem_map); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int wl1251_plt_init(struct wl12xx *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = wl1251_hw_init_mem_config(wl); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = wl12xx_cmd_data_path(wl, wl->channel, 1); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void wl1251_setup(struct wl12xx *wl) | ||||
| { | ||||
| 	/* FIXME: Is it better to use strncpy here or is this ok? */ | ||||
| 	wl->chip.fw_filename = WL1251_FW_NAME; | ||||
| 	wl->chip.nvs_filename = WL1251_NVS_NAME; | ||||
| 
 | ||||
| 	/* Now we know what chip we're using, so adjust the power on sleep
 | ||||
| 	 * time accordingly */ | ||||
| 	wl->chip.power_on_sleep = WL1251_POWER_ON_SLEEP; | ||||
| 
 | ||||
| 	wl->chip.intr_cmd_complete = WL1251_ACX_INTR_CMD_COMPLETE; | ||||
| 	wl->chip.intr_init_complete = WL1251_ACX_INTR_INIT_COMPLETE; | ||||
| 
 | ||||
| 	wl->chip.op_upload_nvs = wl1251_upload_nvs; | ||||
| 	wl->chip.op_upload_fw = wl1251_upload_firmware; | ||||
| 	wl->chip.op_boot = wl1251_boot; | ||||
| 	wl->chip.op_set_ecpu_ctrl = wl1251_set_ecpu_ctrl; | ||||
| 	wl->chip.op_target_enable_interrupts = wl1251_target_enable_interrupts; | ||||
| 	wl->chip.op_hw_init = wl1251_hw_init; | ||||
| 	wl->chip.op_plt_init = wl1251_plt_init; | ||||
| 
 | ||||
| 	wl->chip.p_table = wl1251_part_table; | ||||
| 	wl->chip.acx_reg_table = wl1251_acx_reg_table; | ||||
| 
 | ||||
| 	INIT_WORK(&wl->irq_work, wl1251_irq_work); | ||||
| } | ||||
							
								
								
									
										165
									
								
								drivers/net/wireless/wl12xx/wl1251.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								drivers/net/wireless/wl12xx/wl1251.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,165 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2008 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL1251_H__ | ||||
| #define __WL1251_H__ | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| 
 | ||||
| #include "wl12xx.h" | ||||
| #include "acx.h" | ||||
| 
 | ||||
| #define WL1251_FW_NAME "wl1251-fw.bin" | ||||
| #define WL1251_NVS_NAME "wl1251-nvs.bin" | ||||
| 
 | ||||
| #define WL1251_POWER_ON_SLEEP 10 /* in miliseconds */ | ||||
| 
 | ||||
| void wl1251_setup(struct wl12xx *wl); | ||||
| 
 | ||||
| 
 | ||||
| struct wl1251_acx_memory { | ||||
| 	__le16 num_stations; /* number of STAs to be supported. */ | ||||
| 	u16 reserved_1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Nmber of memory buffers for the RX mem pool. | ||||
| 	 * The actual number may be less if there are | ||||
| 	 * not enough blocks left for the minimum num | ||||
| 	 * of TX ones. | ||||
| 	 */ | ||||
| 	u8 rx_mem_block_num; | ||||
| 	u8 reserved_2; | ||||
| 	u8 num_tx_queues; /* From 1 to 16 */ | ||||
| 	u8 host_if_options; /* HOST_IF* */ | ||||
| 	u8 tx_min_mem_block_num; | ||||
| 	u8 num_ssid_profiles; | ||||
| 	__le16 debug_buffer_size; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| #define ACX_RX_DESC_MIN                1 | ||||
| #define ACX_RX_DESC_MAX                127 | ||||
| #define ACX_RX_DESC_DEF                32 | ||||
| struct wl1251_acx_rx_queue_config { | ||||
| 	u8 num_descs; | ||||
| 	u8 pad; | ||||
| 	u8 type; | ||||
| 	u8 priority; | ||||
| 	__le32 dma_address; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| #define ACX_TX_DESC_MIN                1 | ||||
| #define ACX_TX_DESC_MAX                127 | ||||
| #define ACX_TX_DESC_DEF                16 | ||||
| struct wl1251_acx_tx_queue_config { | ||||
|     u8 num_descs; | ||||
|     u8 pad[2]; | ||||
|     u8 attributes; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| #define MAX_TX_QUEUE_CONFIGS 5 | ||||
| #define MAX_TX_QUEUES 4 | ||||
| struct wl1251_acx_config_memory { | ||||
| 	struct acx_header header; | ||||
| 
 | ||||
| 	struct wl1251_acx_memory mem_config; | ||||
| 	struct wl1251_acx_rx_queue_config rx_queue_config; | ||||
| 	struct wl1251_acx_tx_queue_config tx_queue_config[MAX_TX_QUEUE_CONFIGS]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl1251_acx_mem_map { | ||||
| 	struct acx_header header; | ||||
| 
 | ||||
| 	void *code_start; | ||||
| 	void *code_end; | ||||
| 
 | ||||
| 	void *wep_defkey_start; | ||||
| 	void *wep_defkey_end; | ||||
| 
 | ||||
| 	void *sta_table_start; | ||||
| 	void *sta_table_end; | ||||
| 
 | ||||
| 	void *packet_template_start; | ||||
| 	void *packet_template_end; | ||||
| 
 | ||||
| 	void *queue_memory_start; | ||||
| 	void *queue_memory_end; | ||||
| 
 | ||||
| 	void *packet_memory_pool_start; | ||||
| 	void *packet_memory_pool_end; | ||||
| 
 | ||||
| 	void *debug_buffer1_start; | ||||
| 	void *debug_buffer1_end; | ||||
| 
 | ||||
| 	void *debug_buffer2_start; | ||||
| 	void *debug_buffer2_end; | ||||
| 
 | ||||
| 	/* Number of blocks FW allocated for TX packets */ | ||||
| 	u32 num_tx_mem_blocks; | ||||
| 
 | ||||
| 	/* Number of blocks FW allocated for RX packets */ | ||||
| 	u32 num_rx_mem_blocks; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
| 
 | ||||
|     Host Interrupt Register (WiLink -> Host) | ||||
| 
 | ||||
| **************************************************************************/ | ||||
| 
 | ||||
| /* RX packet is ready in Xfer buffer #0 */ | ||||
| #define WL1251_ACX_INTR_RX0_DATA      BIT(0) | ||||
| 
 | ||||
| /* TX result(s) are in the TX complete buffer */ | ||||
| #define WL1251_ACX_INTR_TX_RESULT	BIT(1) | ||||
| 
 | ||||
| /* OBSOLETE */ | ||||
| #define WL1251_ACX_INTR_TX_XFR		BIT(2) | ||||
| 
 | ||||
| /* RX packet is ready in Xfer buffer #1 */ | ||||
| #define WL1251_ACX_INTR_RX1_DATA	BIT(3) | ||||
| 
 | ||||
| /* Event was entered to Event MBOX #A */ | ||||
| #define WL1251_ACX_INTR_EVENT_A		BIT(4) | ||||
| 
 | ||||
| /* Event was entered to Event MBOX #B */ | ||||
| #define WL1251_ACX_INTR_EVENT_B		BIT(5) | ||||
| 
 | ||||
| /* OBSOLETE */ | ||||
| #define WL1251_ACX_INTR_WAKE_ON_HOST	BIT(6) | ||||
| 
 | ||||
| /* Trace meassge on MBOX #A */ | ||||
| #define WL1251_ACX_INTR_TRACE_A		BIT(7) | ||||
| 
 | ||||
| /* Trace meassge on MBOX #B */ | ||||
| #define WL1251_ACX_INTR_TRACE_B		BIT(8) | ||||
| 
 | ||||
| /* Command processing completion */ | ||||
| #define WL1251_ACX_INTR_CMD_COMPLETE	BIT(9) | ||||
| 
 | ||||
| /* Init sequence is done */ | ||||
| #define WL1251_ACX_INTR_INIT_COMPLETE	BIT(14) | ||||
| 
 | ||||
| #define WL1251_ACX_INTR_ALL           0xFFFFFFFF | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										409
									
								
								drivers/net/wireless/wl12xx/wl12xx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										409
									
								
								drivers/net/wireless/wl12xx/wl12xx.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,409 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (c) 1998-2007 Texas Instruments Incorporated | ||||
|  * Copyright (C) 2008-2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __WL12XX_H__ | ||||
| #define __WL12XX_H__ | ||||
| 
 | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <net/mac80211.h> | ||||
| 
 | ||||
| #define DRIVER_NAME "wl12xx" | ||||
| #define DRIVER_PREFIX DRIVER_NAME ": " | ||||
| 
 | ||||
| enum { | ||||
| 	DEBUG_NONE	= 0, | ||||
| 	DEBUG_IRQ	= BIT(0), | ||||
| 	DEBUG_SPI	= BIT(1), | ||||
| 	DEBUG_BOOT	= BIT(2), | ||||
| 	DEBUG_MAILBOX	= BIT(3), | ||||
| 	DEBUG_NETLINK	= BIT(4), | ||||
| 	DEBUG_EVENT	= BIT(5), | ||||
| 	DEBUG_TX	= BIT(6), | ||||
| 	DEBUG_RX	= BIT(7), | ||||
| 	DEBUG_SCAN	= BIT(8), | ||||
| 	DEBUG_CRYPT	= BIT(9), | ||||
| 	DEBUG_PSM	= BIT(10), | ||||
| 	DEBUG_MAC80211	= BIT(11), | ||||
| 	DEBUG_CMD	= BIT(12), | ||||
| 	DEBUG_ACX	= BIT(13), | ||||
| 	DEBUG_ALL	= ~0, | ||||
| }; | ||||
| 
 | ||||
| #define DEBUG_LEVEL (DEBUG_NONE) | ||||
| 
 | ||||
| #define DEBUG_DUMP_LIMIT 1024 | ||||
| 
 | ||||
| #define wl12xx_error(fmt, arg...) \ | ||||
| 	printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg) | ||||
| 
 | ||||
| #define wl12xx_warning(fmt, arg...) \ | ||||
| 	printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg) | ||||
| 
 | ||||
| #define wl12xx_notice(fmt, arg...) \ | ||||
| 	printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg) | ||||
| 
 | ||||
| #define wl12xx_info(fmt, arg...) \ | ||||
| 	printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg) | ||||
| 
 | ||||
| #define wl12xx_debug(level, fmt, arg...) \ | ||||
| 	do { \ | ||||
| 		if (level & DEBUG_LEVEL) \ | ||||
| 			printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define wl12xx_dump(level, prefix, buf, len)	\ | ||||
| 	do { \ | ||||
| 		if (level & DEBUG_LEVEL) \ | ||||
| 			print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ | ||||
| 				       DUMP_PREFIX_OFFSET, 16, 1,	\ | ||||
| 				       buf,				\ | ||||
| 				       min_t(size_t, len, DEBUG_DUMP_LIMIT), \ | ||||
| 				       0);				\ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define wl12xx_dump_ascii(level, prefix, buf, len)	\ | ||||
| 	do { \ | ||||
| 		if (level & DEBUG_LEVEL) \ | ||||
| 			print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ | ||||
| 				       DUMP_PREFIX_OFFSET, 16, 1,	\ | ||||
| 				       buf,				\ | ||||
| 				       min_t(size_t, len, DEBUG_DUMP_LIMIT), \ | ||||
| 				       true);				\ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #define WL12XX_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN |	\ | ||||
| 				  CFG_BSSID_FILTER_EN) | ||||
| 
 | ||||
| #define WL12XX_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN |  \ | ||||
| 				  CFG_RX_MGMT_EN |  \ | ||||
| 				  CFG_RX_DATA_EN |  \ | ||||
| 				  CFG_RX_CTL_EN |   \ | ||||
| 				  CFG_RX_BCN_EN |   \ | ||||
| 				  CFG_RX_AUTH_EN |  \ | ||||
| 				  CFG_RX_ASSOC_EN) | ||||
| 
 | ||||
| 
 | ||||
| struct boot_attr { | ||||
| 	u32 radio_type; | ||||
| 	u8 mac_clock; | ||||
| 	u8 arm_clock; | ||||
| 	int firmware_debug; | ||||
| 	u32 minor; | ||||
| 	u32 major; | ||||
| 	u32 bugfix; | ||||
| }; | ||||
| 
 | ||||
| enum wl12xx_state { | ||||
| 	WL12XX_STATE_OFF, | ||||
| 	WL12XX_STATE_ON, | ||||
| 	WL12XX_STATE_PLT, | ||||
| }; | ||||
| 
 | ||||
| enum wl12xx_partition_type { | ||||
| 	PART_DOWN, | ||||
| 	PART_WORK, | ||||
| 	PART_DRPW, | ||||
| 
 | ||||
| 	PART_TABLE_LEN | ||||
| }; | ||||
| 
 | ||||
| struct wl12xx_partition { | ||||
| 	u32 size; | ||||
| 	u32 start; | ||||
| }; | ||||
| 
 | ||||
| struct wl12xx_partition_set { | ||||
| 	struct wl12xx_partition mem; | ||||
| 	struct wl12xx_partition reg; | ||||
| }; | ||||
| 
 | ||||
| struct wl12xx; | ||||
| 
 | ||||
| /* FIXME: I'm not sure about this structure name */ | ||||
| struct wl12xx_chip { | ||||
| 	u32 id; | ||||
| 
 | ||||
| 	const char *fw_filename; | ||||
| 	const char *nvs_filename; | ||||
| 
 | ||||
| 	char fw_ver[21]; | ||||
| 
 | ||||
| 	unsigned int power_on_sleep; | ||||
| 	int intr_cmd_complete; | ||||
| 	int intr_init_complete; | ||||
| 
 | ||||
| 	int (*op_upload_fw)(struct wl12xx *wl); | ||||
| 	int (*op_upload_nvs)(struct wl12xx *wl); | ||||
| 	int (*op_boot)(struct wl12xx *wl); | ||||
| 	void (*op_set_ecpu_ctrl)(struct wl12xx *wl, u32 flag); | ||||
| 	void (*op_target_enable_interrupts)(struct wl12xx *wl); | ||||
| 	int (*op_hw_init)(struct wl12xx *wl); | ||||
| 	int (*op_plt_init)(struct wl12xx *wl); | ||||
| 
 | ||||
| 	struct wl12xx_partition_set *p_table; | ||||
| 	enum wl12xx_acx_int_reg *acx_reg_table; | ||||
| }; | ||||
| 
 | ||||
| struct wl12xx_stats { | ||||
| 	struct acx_statistics *fw_stats; | ||||
| 	unsigned long fw_stats_update; | ||||
| 
 | ||||
| 	unsigned int retry_count; | ||||
| 	unsigned int excessive_retries; | ||||
| }; | ||||
| 
 | ||||
| struct wl12xx_debugfs { | ||||
| 	struct dentry *rootdir; | ||||
| 	struct dentry *fw_statistics; | ||||
| 
 | ||||
| 	struct dentry *tx_internal_desc_overflow; | ||||
| 
 | ||||
| 	struct dentry *rx_out_of_mem; | ||||
| 	struct dentry *rx_hdr_overflow; | ||||
| 	struct dentry *rx_hw_stuck; | ||||
| 	struct dentry *rx_dropped; | ||||
| 	struct dentry *rx_fcs_err; | ||||
| 	struct dentry *rx_xfr_hint_trig; | ||||
| 	struct dentry *rx_path_reset; | ||||
| 	struct dentry *rx_reset_counter; | ||||
| 
 | ||||
| 	struct dentry *dma_rx_requested; | ||||
| 	struct dentry *dma_rx_errors; | ||||
| 	struct dentry *dma_tx_requested; | ||||
| 	struct dentry *dma_tx_errors; | ||||
| 
 | ||||
| 	struct dentry *isr_cmd_cmplt; | ||||
| 	struct dentry *isr_fiqs; | ||||
| 	struct dentry *isr_rx_headers; | ||||
| 	struct dentry *isr_rx_mem_overflow; | ||||
| 	struct dentry *isr_rx_rdys; | ||||
| 	struct dentry *isr_irqs; | ||||
| 	struct dentry *isr_tx_procs; | ||||
| 	struct dentry *isr_decrypt_done; | ||||
| 	struct dentry *isr_dma0_done; | ||||
| 	struct dentry *isr_dma1_done; | ||||
| 	struct dentry *isr_tx_exch_complete; | ||||
| 	struct dentry *isr_commands; | ||||
| 	struct dentry *isr_rx_procs; | ||||
| 	struct dentry *isr_hw_pm_mode_changes; | ||||
| 	struct dentry *isr_host_acknowledges; | ||||
| 	struct dentry *isr_pci_pm; | ||||
| 	struct dentry *isr_wakeups; | ||||
| 	struct dentry *isr_low_rssi; | ||||
| 
 | ||||
| 	struct dentry *wep_addr_key_count; | ||||
| 	struct dentry *wep_default_key_count; | ||||
| 	/* skipping wep.reserved */ | ||||
| 	struct dentry *wep_key_not_found; | ||||
| 	struct dentry *wep_decrypt_fail; | ||||
| 	struct dentry *wep_packets; | ||||
| 	struct dentry *wep_interrupt; | ||||
| 
 | ||||
| 	struct dentry *pwr_ps_enter; | ||||
| 	struct dentry *pwr_elp_enter; | ||||
| 	struct dentry *pwr_missing_bcns; | ||||
| 	struct dentry *pwr_wake_on_host; | ||||
| 	struct dentry *pwr_wake_on_timer_exp; | ||||
| 	struct dentry *pwr_tx_with_ps; | ||||
| 	struct dentry *pwr_tx_without_ps; | ||||
| 	struct dentry *pwr_rcvd_beacons; | ||||
| 	struct dentry *pwr_power_save_off; | ||||
| 	struct dentry *pwr_enable_ps; | ||||
| 	struct dentry *pwr_disable_ps; | ||||
| 	struct dentry *pwr_fix_tsf_ps; | ||||
| 	/* skipping cont_miss_bcns_spread for now */ | ||||
| 	struct dentry *pwr_rcvd_awake_beacons; | ||||
| 
 | ||||
| 	struct dentry *mic_rx_pkts; | ||||
| 	struct dentry *mic_calc_failure; | ||||
| 
 | ||||
| 	struct dentry *aes_encrypt_fail; | ||||
| 	struct dentry *aes_decrypt_fail; | ||||
| 	struct dentry *aes_encrypt_packets; | ||||
| 	struct dentry *aes_decrypt_packets; | ||||
| 	struct dentry *aes_encrypt_interrupt; | ||||
| 	struct dentry *aes_decrypt_interrupt; | ||||
| 
 | ||||
| 	struct dentry *event_heart_beat; | ||||
| 	struct dentry *event_calibration; | ||||
| 	struct dentry *event_rx_mismatch; | ||||
| 	struct dentry *event_rx_mem_empty; | ||||
| 	struct dentry *event_rx_pool; | ||||
| 	struct dentry *event_oom_late; | ||||
| 	struct dentry *event_phy_transmit_error; | ||||
| 	struct dentry *event_tx_stuck; | ||||
| 
 | ||||
| 	struct dentry *ps_pspoll_timeouts; | ||||
| 	struct dentry *ps_upsd_timeouts; | ||||
| 	struct dentry *ps_upsd_max_sptime; | ||||
| 	struct dentry *ps_upsd_max_apturn; | ||||
| 	struct dentry *ps_pspoll_max_apturn; | ||||
| 	struct dentry *ps_pspoll_utilization; | ||||
| 	struct dentry *ps_upsd_utilization; | ||||
| 
 | ||||
| 	struct dentry *rxpipe_rx_prep_beacon_drop; | ||||
| 	struct dentry *rxpipe_descr_host_int_trig_rx_data; | ||||
| 	struct dentry *rxpipe_beacon_buffer_thres_host_int_trig_rx_data; | ||||
| 	struct dentry *rxpipe_missed_beacon_host_int_trig_rx_data; | ||||
| 	struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data; | ||||
| 
 | ||||
| 	struct dentry *tx_queue_len; | ||||
| 
 | ||||
| 	struct dentry *retry_count; | ||||
| 	struct dentry *excessive_retries; | ||||
| }; | ||||
| 
 | ||||
| struct wl12xx { | ||||
| 	struct ieee80211_hw *hw; | ||||
| 	bool mac80211_registered; | ||||
| 
 | ||||
| 	struct spi_device *spi; | ||||
| 
 | ||||
| 	void (*set_power)(bool enable); | ||||
| 	int irq; | ||||
| 
 | ||||
| 	enum wl12xx_state state; | ||||
| 	struct mutex mutex; | ||||
| 
 | ||||
| 	int physical_mem_addr; | ||||
| 	int physical_reg_addr; | ||||
| 	int virtual_mem_addr; | ||||
| 	int virtual_reg_addr; | ||||
| 
 | ||||
| 	struct wl12xx_chip chip; | ||||
| 
 | ||||
| 	int cmd_box_addr; | ||||
| 	int event_box_addr; | ||||
| 	struct boot_attr boot_attr; | ||||
| 
 | ||||
| 	u8 *fw; | ||||
| 	size_t fw_len; | ||||
| 	u8 *nvs; | ||||
| 	size_t nvs_len; | ||||
| 
 | ||||
| 	u8 bssid[ETH_ALEN]; | ||||
| 	u8 mac_addr[ETH_ALEN]; | ||||
| 	u8 bss_type; | ||||
| 	u8 listen_int; | ||||
| 	int channel; | ||||
| 
 | ||||
| 	void *target_mem_map; | ||||
| 	struct acx_data_path_params_resp *data_path; | ||||
| 
 | ||||
| 	/* Number of TX packets transferred to the FW, modulo 16 */ | ||||
| 	u32 data_in_count; | ||||
| 
 | ||||
| 	/* Frames scheduled for transmission, not handled yet */ | ||||
| 	struct sk_buff_head tx_queue; | ||||
| 	bool tx_queue_stopped; | ||||
| 
 | ||||
| 	struct work_struct tx_work; | ||||
| 	struct work_struct filter_work; | ||||
| 
 | ||||
| 	/* Pending TX frames */ | ||||
| 	struct sk_buff *tx_frames[16]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Index pointing to the next TX complete entry | ||||
| 	 * in the cyclic XT complete array we get from | ||||
| 	 * the FW. | ||||
| 	 */ | ||||
| 	u32 next_tx_complete; | ||||
| 
 | ||||
| 	/* FW Rx counter */ | ||||
| 	u32 rx_counter; | ||||
| 
 | ||||
| 	/* Rx frames handled */ | ||||
| 	u32 rx_handled; | ||||
| 
 | ||||
| 	/* Current double buffer */ | ||||
| 	u32 rx_current_buffer; | ||||
| 	u32 rx_last_id; | ||||
| 
 | ||||
| 	/* The target interrupt mask */ | ||||
| 	u32 intr_mask; | ||||
| 	struct work_struct irq_work; | ||||
| 
 | ||||
| 	/* The mbox event mask */ | ||||
| 	u32 event_mask; | ||||
| 
 | ||||
| 	/* Mailbox pointers */ | ||||
| 	u32 mbox_ptr[2]; | ||||
| 
 | ||||
| 	/* Are we currently scanning */ | ||||
| 	bool scanning; | ||||
| 
 | ||||
| 	/* Our association ID */ | ||||
| 	u16 aid; | ||||
| 
 | ||||
| 	/* Default key (for WEP) */ | ||||
| 	u32 default_key; | ||||
| 
 | ||||
| 	unsigned int tx_mgmt_frm_rate; | ||||
| 	unsigned int tx_mgmt_frm_mod; | ||||
| 
 | ||||
| 	unsigned int rx_config; | ||||
| 	unsigned int rx_filter; | ||||
| 
 | ||||
| 	/* is firmware in elp mode */ | ||||
| 	bool elp; | ||||
| 
 | ||||
| 	/* we can be in psm, but not in elp, we have to differentiate */ | ||||
| 	bool psm; | ||||
| 
 | ||||
| 	/* PSM mode requested */ | ||||
| 	bool psm_requested; | ||||
| 
 | ||||
| 	/* in dBm */ | ||||
| 	int power_level; | ||||
| 
 | ||||
| 	struct wl12xx_stats stats; | ||||
| 	struct wl12xx_debugfs debugfs; | ||||
| }; | ||||
| 
 | ||||
| int wl12xx_plt_start(struct wl12xx *wl); | ||||
| int wl12xx_plt_stop(struct wl12xx *wl); | ||||
| 
 | ||||
| #define DEFAULT_HW_GEN_MODULATION_TYPE    CCK_LONG /* Long Preamble */ | ||||
| #define DEFAULT_HW_GEN_TX_RATE          RATE_2MBPS | ||||
| #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ | ||||
| 
 | ||||
| #define WL12XX_DEFAULT_POWER_LEVEL 20 | ||||
| 
 | ||||
| #define WL12XX_TX_QUEUE_MAX_LENGTH 20 | ||||
| 
 | ||||
| /* Different chips need different sleep times after power on.  WL1271 needs
 | ||||
|  * 200ms, WL1251 needs only 10ms.  By default we use 200ms, but as soon as we | ||||
|  * know the chip ID, we change the sleep value in the wl12xx chip structure, | ||||
|  * so in subsequent power ons, we don't waste more time then needed.  */ | ||||
| #define WL12XX_DEFAULT_POWER_ON_SLEEP 200 | ||||
| 
 | ||||
| #define CHIP_ID_1251_PG10	           (0x7010101) | ||||
| #define CHIP_ID_1251_PG11	           (0x7020101) | ||||
| #define CHIP_ID_1251_PG12	           (0x7030101) | ||||
| #define CHIP_ID_1271_PG10	           (0x4030101) | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										156
									
								
								drivers/net/wireless/wl12xx/wl12xx_80211.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								drivers/net/wireless/wl12xx/wl12xx_80211.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | ||||
| #ifndef __WL12XX_80211_H__ | ||||
| #define __WL12XX_80211_H__ | ||||
| 
 | ||||
| #include <linux/if_ether.h>	/* ETH_ALEN */ | ||||
| 
 | ||||
| /* RATES */ | ||||
| #define IEEE80211_CCK_RATE_1MB		        0x02 | ||||
| #define IEEE80211_CCK_RATE_2MB		        0x04 | ||||
| #define IEEE80211_CCK_RATE_5MB		        0x0B | ||||
| #define IEEE80211_CCK_RATE_11MB		        0x16 | ||||
| #define IEEE80211_OFDM_RATE_6MB		        0x0C | ||||
| #define IEEE80211_OFDM_RATE_9MB		        0x12 | ||||
| #define IEEE80211_OFDM_RATE_12MB		0x18 | ||||
| #define IEEE80211_OFDM_RATE_18MB		0x24 | ||||
| #define IEEE80211_OFDM_RATE_24MB		0x30 | ||||
| #define IEEE80211_OFDM_RATE_36MB		0x48 | ||||
| #define IEEE80211_OFDM_RATE_48MB		0x60 | ||||
| #define IEEE80211_OFDM_RATE_54MB		0x6C | ||||
| #define IEEE80211_BASIC_RATE_MASK		0x80 | ||||
| 
 | ||||
| #define IEEE80211_CCK_RATE_1MB_MASK		(1<<0) | ||||
| #define IEEE80211_CCK_RATE_2MB_MASK		(1<<1) | ||||
| #define IEEE80211_CCK_RATE_5MB_MASK		(1<<2) | ||||
| #define IEEE80211_CCK_RATE_11MB_MASK		(1<<3) | ||||
| #define IEEE80211_OFDM_RATE_6MB_MASK		(1<<4) | ||||
| #define IEEE80211_OFDM_RATE_9MB_MASK		(1<<5) | ||||
| #define IEEE80211_OFDM_RATE_12MB_MASK		(1<<6) | ||||
| #define IEEE80211_OFDM_RATE_18MB_MASK		(1<<7) | ||||
| #define IEEE80211_OFDM_RATE_24MB_MASK		(1<<8) | ||||
| #define IEEE80211_OFDM_RATE_36MB_MASK		(1<<9) | ||||
| #define IEEE80211_OFDM_RATE_48MB_MASK		(1<<10) | ||||
| #define IEEE80211_OFDM_RATE_54MB_MASK		(1<<11) | ||||
| 
 | ||||
| #define IEEE80211_CCK_RATES_MASK	  0x0000000F | ||||
| #define IEEE80211_CCK_BASIC_RATES_MASK	 (IEEE80211_CCK_RATE_1MB_MASK | \ | ||||
| 	IEEE80211_CCK_RATE_2MB_MASK) | ||||
| #define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ | ||||
| 	IEEE80211_CCK_RATE_5MB_MASK | \ | ||||
| 	IEEE80211_CCK_RATE_11MB_MASK) | ||||
| 
 | ||||
| #define IEEE80211_OFDM_RATES_MASK	  0x00000FF0 | ||||
| #define IEEE80211_OFDM_BASIC_RATES_MASK	  (IEEE80211_OFDM_RATE_6MB_MASK | \ | ||||
| 	IEEE80211_OFDM_RATE_12MB_MASK | \ | ||||
| 	IEEE80211_OFDM_RATE_24MB_MASK) | ||||
| #define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ | ||||
| 	IEEE80211_OFDM_RATE_9MB_MASK  | \ | ||||
| 	IEEE80211_OFDM_RATE_18MB_MASK | \ | ||||
| 	IEEE80211_OFDM_RATE_36MB_MASK | \ | ||||
| 	IEEE80211_OFDM_RATE_48MB_MASK | \ | ||||
| 	IEEE80211_OFDM_RATE_54MB_MASK) | ||||
| #define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ | ||||
| 				      IEEE80211_CCK_DEFAULT_RATES_MASK) | ||||
| 
 | ||||
| 
 | ||||
| /* This really should be 8, but not for our firmware */ | ||||
| #define MAX_SUPPORTED_RATES 32 | ||||
| #define COUNTRY_STRING_LEN 3 | ||||
| #define MAX_COUNTRY_TRIPLETS 32 | ||||
| 
 | ||||
| /* Headers */ | ||||
| struct ieee80211_header { | ||||
| 	__le16 frame_ctl; | ||||
| 	__le16 duration_id; | ||||
| 	u8 da[ETH_ALEN]; | ||||
| 	u8 sa[ETH_ALEN]; | ||||
| 	u8 bssid[ETH_ALEN]; | ||||
| 	__le16 seq_ctl; | ||||
| 	u8 payload[0]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_ie_header { | ||||
| 	u8 id; | ||||
| 	u8 len; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| /* IEs */ | ||||
| 
 | ||||
| struct wl12xx_ie_ssid { | ||||
| 	struct wl12xx_ie_header header; | ||||
| 	char ssid[IW_ESSID_MAX_SIZE]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_ie_rates { | ||||
| 	struct wl12xx_ie_header header; | ||||
| 	u8 rates[MAX_SUPPORTED_RATES]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_ie_ds_params { | ||||
| 	struct wl12xx_ie_header header; | ||||
| 	u8 channel; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct country_triplet { | ||||
| 	u8 channel; | ||||
| 	u8 num_channels; | ||||
| 	u8 max_tx_power; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_ie_country { | ||||
| 	struct wl12xx_ie_header header; | ||||
| 	u8 country_string[COUNTRY_STRING_LEN]; | ||||
| 	struct country_triplet triplets[MAX_COUNTRY_TRIPLETS]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| /* Templates */ | ||||
| 
 | ||||
| struct wl12xx_beacon_template { | ||||
| 	struct ieee80211_header header; | ||||
| 	__le32 time_stamp[2]; | ||||
| 	__le16 beacon_interval; | ||||
| 	__le16 capability; | ||||
| 	struct wl12xx_ie_ssid ssid; | ||||
| 	struct wl12xx_ie_rates rates; | ||||
| 	struct wl12xx_ie_rates ext_rates; | ||||
| 	struct wl12xx_ie_ds_params ds_params; | ||||
| 	struct wl12xx_ie_country country; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_null_data_template { | ||||
| 	struct ieee80211_header header; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_ps_poll_template { | ||||
| 	u16 fc; | ||||
| 	u16 aid; | ||||
| 	u8 bssid[ETH_ALEN]; | ||||
| 	u8 ta[ETH_ALEN]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_qos_null_data_template { | ||||
| 	struct ieee80211_header header; | ||||
| 	__le16 qos_ctl; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| struct wl12xx_probe_req_template { | ||||
| 	struct ieee80211_header header; | ||||
| 	struct wl12xx_ie_ssid ssid; | ||||
| 	struct wl12xx_ie_rates rates; | ||||
| 	struct wl12xx_ie_rates ext_rates; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| 
 | ||||
| struct wl12xx_probe_resp_template { | ||||
| 	struct ieee80211_header header; | ||||
| 	__le32 time_stamp[2]; | ||||
| 	__le16 beacon_interval; | ||||
| 	__le16 capability; | ||||
| 	struct wl12xx_ie_ssid ssid; | ||||
| 	struct wl12xx_ie_rates rates; | ||||
| 	struct wl12xx_ie_rates ext_rates; | ||||
| 	struct wl12xx_ie_ds_params ds_params; | ||||
| 	struct wl12xx_ie_country country; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| #endif | ||||
| @ -755,52 +755,6 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed) | ||||
| 	return zd_chip_set_channel(&mac->chip, conf->channel->hw_value); | ||||
| } | ||||
| 
 | ||||
| static int zd_op_config_interface(struct ieee80211_hw *hw, | ||||
| 				  struct ieee80211_vif *vif, | ||||
| 				   struct ieee80211_if_conf *conf) | ||||
| { | ||||
| 	struct zd_mac *mac = zd_hw_mac(hw); | ||||
| 	int associated; | ||||
| 	int r; | ||||
| 
 | ||||
| 	if (mac->type == NL80211_IFTYPE_MESH_POINT || | ||||
| 	    mac->type == NL80211_IFTYPE_ADHOC) { | ||||
| 		associated = true; | ||||
| 		if (conf->changed & IEEE80211_IFCC_BEACON) { | ||||
| 			struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||||
| 
 | ||||
| 			if (!beacon) | ||||
| 				return -ENOMEM; | ||||
| 			r = zd_mac_config_beacon(hw, beacon); | ||||
| 			kfree_skb(beacon); | ||||
| 
 | ||||
| 			if (r < 0) | ||||
| 				return r; | ||||
| 		} | ||||
| 
 | ||||
| 		if (conf->changed & IEEE80211_IFCC_BEACON_ENABLED) { | ||||
| 			u32 interval; | ||||
| 
 | ||||
| 			if (conf->enable_beacon) | ||||
| 				interval = BCN_MODE_IBSS | hw->conf.beacon_int; | ||||
| 			else | ||||
| 				interval = 0; | ||||
| 
 | ||||
| 			r = zd_set_beacon_interval(&mac->chip, interval); | ||||
| 			if (r < 0) | ||||
| 				return r; | ||||
| 		} | ||||
| 	} else | ||||
| 		associated = is_valid_ether_addr(conf->bssid); | ||||
| 
 | ||||
| 	spin_lock_irq(&mac->lock); | ||||
| 	mac->associated = associated; | ||||
| 	spin_unlock_irq(&mac->lock); | ||||
| 
 | ||||
| 	/* TODO: do hardware bssid filtering */ | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void zd_process_intr(struct work_struct *work) | ||||
| { | ||||
| 	u16 int_status; | ||||
| @ -923,9 +877,42 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw, | ||||
| { | ||||
| 	struct zd_mac *mac = zd_hw_mac(hw); | ||||
| 	unsigned long flags; | ||||
| 	int associated; | ||||
| 
 | ||||
| 	dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); | ||||
| 
 | ||||
| 	if (mac->type == NL80211_IFTYPE_MESH_POINT || | ||||
| 	    mac->type == NL80211_IFTYPE_ADHOC) { | ||||
| 		associated = true; | ||||
| 		if (changes & BSS_CHANGED_BEACON) { | ||||
| 			struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||||
| 
 | ||||
| 			if (beacon) { | ||||
| 				zd_mac_config_beacon(hw, beacon); | ||||
| 				kfree_skb(beacon); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (changes & BSS_CHANGED_BEACON_ENABLED) { | ||||
| 			u32 interval; | ||||
| 
 | ||||
| 			if (bss_conf->enable_beacon) | ||||
| 				interval = BCN_MODE_IBSS | | ||||
| 						bss_conf->beacon_int; | ||||
| 			else | ||||
| 				interval = 0; | ||||
| 
 | ||||
| 			zd_set_beacon_interval(&mac->chip, interval); | ||||
| 		} | ||||
| 	} else | ||||
| 		associated = is_valid_ether_addr(bss_conf->bssid); | ||||
| 
 | ||||
| 	spin_lock_irq(&mac->lock); | ||||
| 	mac->associated = associated; | ||||
| 	spin_unlock_irq(&mac->lock); | ||||
| 
 | ||||
| 	/* TODO: do hardware bssid filtering */ | ||||
| 
 | ||||
| 	if (changes & BSS_CHANGED_ERP_PREAMBLE) { | ||||
| 		spin_lock_irqsave(&mac->lock, flags); | ||||
| 		mac->short_preamble = bss_conf->use_short_preamble; | ||||
| @ -952,7 +939,6 @@ static const struct ieee80211_ops zd_ops = { | ||||
| 	.add_interface		= zd_op_add_interface, | ||||
| 	.remove_interface	= zd_op_remove_interface, | ||||
| 	.config			= zd_op_config, | ||||
| 	.config_interface	= zd_op_config_interface, | ||||
| 	.configure_filter	= zd_op_configure_filter, | ||||
| 	.bss_info_changed	= zd_op_bss_info_changed, | ||||
| 	.get_tsf		= zd_op_get_tsf, | ||||
|  | ||||
| @ -543,7 +543,7 @@ struct ieee80211_tim_ie { | ||||
| 	u8 virtual_map[1]; | ||||
| } __attribute__ ((packed)); | ||||
| 
 | ||||
| #define WLAN_SA_QUERY_TR_ID_LEN 16 | ||||
| #define WLAN_SA_QUERY_TR_ID_LEN 2 | ||||
| 
 | ||||
| struct ieee80211_mgmt { | ||||
| 	__le16 frame_control; | ||||
|  | ||||
							
								
								
									
										31
									
								
								include/linux/spi/wl12xx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								include/linux/spi/wl12xx.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| /*
 | ||||
|  * This file is part of wl12xx | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * | ||||
|  * Contact: Kalle Valo <kalle.valo@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License | ||||
|  * version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||
|  * 02110-1301 USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _LINUX_SPI_WL12XX_H | ||||
| #define _LINUX_SPI_WL12XX_H | ||||
| 
 | ||||
| struct wl12xx_platform_data { | ||||
| 	void (*set_power)(bool enable); | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| @ -149,6 +149,13 @@ struct ieee80211_low_level_stats { | ||||
|  * @BSS_CHANGED_ERP_SLOT: slot timing changed | ||||
|  * @BSS_CHANGED_HT: 802.11n parameters changed | ||||
|  * @BSS_CHANGED_BASIC_RATES: Basic rateset changed | ||||
|  * @BSS_CHANGED_BEACON_INT: Beacon interval changed | ||||
|  * @BSS_CHANGED_BSSID: BSSID changed, for whatever | ||||
|  *	reason (IBSS and managed mode) | ||||
|  * @BSS_CHANGED_BEACON: Beacon data changed, retrieve | ||||
|  *	new beacon (beaconing modes) | ||||
|  * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be | ||||
|  *	enabled/disabled (beaconing modes) | ||||
|  */ | ||||
| enum ieee80211_bss_change { | ||||
| 	BSS_CHANGED_ASSOC		= 1<<0, | ||||
| @ -157,6 +164,10 @@ enum ieee80211_bss_change { | ||||
| 	BSS_CHANGED_ERP_SLOT		= 1<<3, | ||||
| 	BSS_CHANGED_HT                  = 1<<4, | ||||
| 	BSS_CHANGED_BASIC_RATES		= 1<<5, | ||||
| 	BSS_CHANGED_BEACON_INT		= 1<<6, | ||||
| 	BSS_CHANGED_BSSID		= 1<<7, | ||||
| 	BSS_CHANGED_BEACON		= 1<<8, | ||||
| 	BSS_CHANGED_BEACON_ENABLED	= 1<<9, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
| @ -190,8 +201,11 @@ struct ieee80211_bss_ht_conf { | ||||
|  * @basic_rates: bitmap of basic rates, each bit stands for an | ||||
|  *	index into the rate table configured by the driver in | ||||
|  *	the current band. | ||||
|  * @bssid: The BSSID for this BSS | ||||
|  * @enable_beacon: whether beaconing should be enabled or not | ||||
|  */ | ||||
| struct ieee80211_bss_conf { | ||||
| 	const u8 *bssid; | ||||
| 	/* association related data */ | ||||
| 	bool assoc; | ||||
| 	u16 aid; | ||||
| @ -199,6 +213,7 @@ struct ieee80211_bss_conf { | ||||
| 	bool use_cts_prot; | ||||
| 	bool use_short_preamble; | ||||
| 	bool use_short_slot; | ||||
| 	bool enable_beacon; | ||||
| 	u8 dtim_period; | ||||
| 	u16 beacon_int; | ||||
| 	u16 assoc_capability; | ||||
| @ -518,10 +533,16 @@ struct ieee80211_rx_status { | ||||
|  * | ||||
|  * @IEEE80211_CONF_RADIOTAP: add radiotap header at receive time (if supported) | ||||
|  * @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only) | ||||
|  * @IEEE80211_CONF_IDLE: The device is running, but idle; if the flag is set | ||||
|  *	the driver should be prepared to handle configuration requests but | ||||
|  *	may turn the device off as much as possible. Typically, this flag will | ||||
|  *	be set when an interface is set UP but not associated or scanning, but | ||||
|  *	it can also be unset in that case when monitor interfaces are active. | ||||
|  */ | ||||
| enum ieee80211_conf_flags { | ||||
| 	IEEE80211_CONF_RADIOTAP		= (1<<0), | ||||
| 	IEEE80211_CONF_PS		= (1<<1), | ||||
| 	IEEE80211_CONF_IDLE		= (1<<2), | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| @ -529,25 +550,35 @@ enum ieee80211_conf_flags { | ||||
|  * enum ieee80211_conf_changed - denotes which configuration changed | ||||
|  * | ||||
|  * @IEEE80211_CONF_CHANGE_RADIO_ENABLED: the value of radio_enabled changed | ||||
|  * @IEEE80211_CONF_CHANGE_BEACON_INTERVAL: the beacon interval changed | ||||
|  * @_IEEE80211_CONF_CHANGE_BEACON_INTERVAL: DEPRECATED | ||||
|  * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed | ||||
|  * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed | ||||
|  * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed | ||||
|  * @IEEE80211_CONF_CHANGE_POWER: the TX power changed | ||||
|  * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed | ||||
|  * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed | ||||
|  * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed | ||||
|  */ | ||||
| enum ieee80211_conf_changed { | ||||
| 	IEEE80211_CONF_CHANGE_RADIO_ENABLED	= BIT(0), | ||||
| 	IEEE80211_CONF_CHANGE_BEACON_INTERVAL	= BIT(1), | ||||
| 	_IEEE80211_CONF_CHANGE_BEACON_INTERVAL	= BIT(1), | ||||
| 	IEEE80211_CONF_CHANGE_LISTEN_INTERVAL	= BIT(2), | ||||
| 	IEEE80211_CONF_CHANGE_RADIOTAP		= BIT(3), | ||||
| 	IEEE80211_CONF_CHANGE_PS		= BIT(4), | ||||
| 	IEEE80211_CONF_CHANGE_POWER		= BIT(5), | ||||
| 	IEEE80211_CONF_CHANGE_CHANNEL		= BIT(6), | ||||
| 	IEEE80211_CONF_CHANGE_RETRY_LIMITS	= BIT(7), | ||||
| 	IEEE80211_CONF_CHANGE_IDLE		= BIT(8), | ||||
| }; | ||||
| 
 | ||||
| static inline __deprecated enum ieee80211_conf_changed | ||||
| __IEEE80211_CONF_CHANGE_BEACON_INTERVAL(void) | ||||
| { | ||||
| 	return _IEEE80211_CONF_CHANGE_BEACON_INTERVAL; | ||||
| } | ||||
| #define IEEE80211_CONF_CHANGE_BEACON_INTERVAL \ | ||||
| 	__IEEE80211_CONF_CHANGE_BEACON_INTERVAL() | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ieee80211_conf - configuration of the device | ||||
|  * | ||||
| @ -559,7 +590,7 @@ enum ieee80211_conf_changed { | ||||
|  * @beacon_int: beacon interval (TODO make interface config) | ||||
|  * | ||||
|  * @listen_interval: listen interval in units of beacon interval | ||||
|  * @max_sleep_interval: the maximum number of beacon intervals to sleep for | ||||
|  * @max_sleep_period: the maximum number of beacon intervals to sleep for | ||||
|  *	before checking the beacon for a TIM bit (managed mode only); this | ||||
|  *	value will be only achievable between DTIM frames, the hardware | ||||
|  *	needs to check for the multicast traffic bit in DTIM beacons. | ||||
| @ -584,7 +615,7 @@ struct ieee80211_conf { | ||||
| 	int beacon_int; | ||||
| 	u32 flags; | ||||
| 	int power_level, dynamic_ps_timeout; | ||||
| 	int max_sleep_interval; | ||||
| 	int max_sleep_period; | ||||
| 
 | ||||
| 	u16 listen_interval; | ||||
| 	bool radio_enabled; | ||||
| @ -649,37 +680,6 @@ struct ieee80211_if_init_conf { | ||||
| 	void *mac_addr; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ieee80211_if_conf_change - interface config change flags | ||||
|  * | ||||
|  * @IEEE80211_IFCC_BSSID: The BSSID changed. | ||||
|  * @IEEE80211_IFCC_BEACON: The beacon for this interface changed | ||||
|  *	(currently AP and MESH only), use ieee80211_beacon_get(). | ||||
|  * @IEEE80211_IFCC_BEACON_ENABLED: The enable_beacon value changed. | ||||
|  */ | ||||
| enum ieee80211_if_conf_change { | ||||
| 	IEEE80211_IFCC_BSSID		= BIT(0), | ||||
| 	IEEE80211_IFCC_BEACON		= BIT(1), | ||||
| 	IEEE80211_IFCC_BEACON_ENABLED	= BIT(2), | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct ieee80211_if_conf - configuration of an interface | ||||
|  * | ||||
|  * @changed: parameters that have changed, see &enum ieee80211_if_conf_change. | ||||
|  * @bssid: BSSID of the network we are associated to/creating. | ||||
|  * @enable_beacon: Indicates whether beacons can be sent. | ||||
|  *	This is valid only for AP/IBSS/MESH modes. | ||||
|  * | ||||
|  * This structure is passed to the config_interface() callback of | ||||
|  * &struct ieee80211_hw. | ||||
|  */ | ||||
| struct ieee80211_if_conf { | ||||
| 	u32 changed; | ||||
| 	const u8 *bssid; | ||||
| 	bool enable_beacon; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * enum ieee80211_key_alg - key algorithm | ||||
|  * @ALG_WEP: WEP40 or WEP104 | ||||
| @ -1348,10 +1348,6 @@ enum ieee80211_ampdu_mlme_action { | ||||
|  *	This function should never fail but returns a negative error code | ||||
|  *	if it does. | ||||
|  * | ||||
|  * @config_interface: Handler for configuration requests related to interfaces | ||||
|  *	(e.g. BSSID changes.) | ||||
|  *	Returns a negative error code which will be seen in userspace. | ||||
|  * | ||||
|  * @bss_info_changed: Handler for configuration requests related to BSS | ||||
|  *	parameters that may vary during BSS's lifespan, and may affect low | ||||
|  *	level driver (e.g. assoc/disassoc status, erp parameters). | ||||
| @ -1453,9 +1449,6 @@ struct ieee80211_ops { | ||||
| 	void (*remove_interface)(struct ieee80211_hw *hw, | ||||
| 				 struct ieee80211_if_init_conf *conf); | ||||
| 	int (*config)(struct ieee80211_hw *hw, u32 changed); | ||||
| 	int (*config_interface)(struct ieee80211_hw *hw, | ||||
| 				struct ieee80211_vif *vif, | ||||
| 				struct ieee80211_if_conf *conf); | ||||
| 	void (*bss_info_changed)(struct ieee80211_hw *hw, | ||||
| 				 struct ieee80211_vif *vif, | ||||
| 				 struct ieee80211_bss_conf *info, | ||||
|  | ||||
| @ -16,12 +16,12 @@ | ||||
| #include <linux/ieee80211.h> | ||||
| #include <net/mac80211.h> | ||||
| #include "ieee80211_i.h" | ||||
| #include "driver-ops.h" | ||||
| 
 | ||||
| void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | ||||
| 				    u16 initiator, u16 reason) | ||||
| { | ||||
| 	struct ieee80211_local *local = sta->local; | ||||
| 	struct ieee80211_hw *hw = &local->hw; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* check if TID is in operational state */ | ||||
| @ -41,8 +41,8 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | ||||
| 	       sta->sta.addr, tid); | ||||
| #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||||
| 
 | ||||
| 	if (local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP, | ||||
| 				     &sta->sta, tid, NULL)) | ||||
| 	if (drv_ampdu_action(local, IEEE80211_AMPDU_RX_STOP, | ||||
| 			     &sta->sta, tid, NULL)) | ||||
| 		printk(KERN_DEBUG "HW problem - can not stop rx " | ||||
| 				"aggregation for tid %d\n", tid); | ||||
| 
 | ||||
| @ -68,6 +68,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | ||||
| 	spin_lock_bh(&sta->lock); | ||||
| 	/* free resources */ | ||||
| 	kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf); | ||||
| 	kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_time); | ||||
| 
 | ||||
| 	if (!sta->ampdu_mlme.tid_rx[tid]->shutdown) { | ||||
| 		kfree(sta->ampdu_mlme.tid_rx[tid]); | ||||
| @ -268,19 +269,23 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | ||||
| 	/* prepare reordering buffer */ | ||||
| 	tid_agg_rx->reorder_buf = | ||||
| 		kcalloc(buf_size, sizeof(struct sk_buff *), GFP_ATOMIC); | ||||
| 	if (!tid_agg_rx->reorder_buf) { | ||||
| 	tid_agg_rx->reorder_time = | ||||
| 		kcalloc(buf_size, sizeof(unsigned long), GFP_ATOMIC); | ||||
| 	if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| 		if (net_ratelimit()) | ||||
| 			printk(KERN_ERR "can not allocate reordering buffer " | ||||
| 			       "to tid %d\n", tid); | ||||
| #endif | ||||
| 		kfree(tid_agg_rx->reorder_buf); | ||||
| 		kfree(tid_agg_rx->reorder_time); | ||||
| 		kfree(sta->ampdu_mlme.tid_rx[tid]); | ||||
| 		sta->ampdu_mlme.tid_rx[tid] = NULL; | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	if (local->ops->ampdu_action) | ||||
| 		ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START, | ||||
| 					       &sta->sta, tid, &start_seq_num); | ||||
| 	ret = drv_ampdu_action(local, 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); | ||||
| #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||||
|  | ||||
| @ -16,6 +16,7 @@ | ||||
| #include <linux/ieee80211.h> | ||||
| #include <net/mac80211.h> | ||||
| #include "ieee80211_i.h" | ||||
| #include "driver-ops.h" | ||||
| #include "wme.h" | ||||
| 
 | ||||
| /**
 | ||||
| @ -134,8 +135,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 = local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_STOP, | ||||
| 				       &sta->sta, tid, NULL); | ||||
| 	ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_STOP, | ||||
| 			       &sta->sta, tid, NULL); | ||||
| 
 | ||||
| 	/* HW shall not deny going back to legacy */ | ||||
| 	if (WARN_ON(ret)) { | ||||
| @ -306,8 +307,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
| 
 | ||||
| 	start_seq_num = sta->tid_seq[tid]; | ||||
| 
 | ||||
| 	ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START, | ||||
| 				       &sta->sta, tid, &start_seq_num); | ||||
| 	ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_START, | ||||
| 			       &sta->sta, tid, &start_seq_num); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| #ifdef CONFIG_MAC80211_HT_DEBUG | ||||
| @ -418,8 +419,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | ||||
| 	ieee80211_agg_splice_finish(local, sta, tid); | ||||
| 	spin_unlock(&local->ampdu_lock); | ||||
| 
 | ||||
| 	local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, | ||||
| 				 &sta->sta, tid, NULL); | ||||
| 	drv_ampdu_action(local, IEEE80211_AMPDU_TX_OPERATIONAL, | ||||
| 			 &sta->sta, tid, NULL); | ||||
| } | ||||
| 
 | ||||
| void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user