Exemplo n.º 1
0
/*
* called from both kernel as from wl_*()
* precondition: perimeter lock is not acquired.
*/
static void wl_remove(struct pci_dev *pdev)
{
	struct wl_info *wl;
	struct ieee80211_hw *hw;
	int status;

	hw = pci_get_drvdata(pdev);
	wl = HW_TO_WL(hw);
	if (!wl) {
		pr_err("wl: wl_remove: pci_get_drvdata failed\n");
		return;
	}

	WL_LOCK(wl);
	status = wlc_chipmatch(pdev->vendor, pdev->device);
	WL_UNLOCK(wl);
	if (!status) {
		wiphy_err(wl->wiphy, "wl: wl_remove: wlc_chipmatch failed\n");
		return;
	}
	if (wl->wlc) {
		wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
		ieee80211_unregister_hw(hw);
		WL_LOCK(wl);
		wl_down(wl);
		WL_UNLOCK(wl);
	}
	pci_disable_device(pdev);

	wl_free(wl);

	pci_set_drvdata(pdev, NULL);
	ieee80211_free_hw(hw);
}
Exemplo n.º 2
0
static int
wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
	struct wl_info *wl;
	int err;

	/* Just STA for now */
	if (vif->type != NL80211_IFTYPE_AP &&
	    vif->type != NL80211_IFTYPE_MESH_POINT &&
	    vif->type != NL80211_IFTYPE_STATION &&
	    vif->type != NL80211_IFTYPE_WDS &&
	    vif->type != NL80211_IFTYPE_ADHOC) {
		wiphy_err(hw->wiphy, "%s: Attempt to add type %d, only"
			  " STA for now\n", __func__, vif->type);
		return -EOPNOTSUPP;
	}

	wl = HW_TO_WL(hw);
	WL_LOCK(wl);
	err = wl_up(wl);
	WL_UNLOCK(wl);

	if (err != 0) {
		wiphy_err(hw->wiphy, "%s: wl_up() returned %d\n", __func__,
			  err);
	}
	return err;
}
Exemplo n.º 3
0
static void wl_ops_flush(struct ieee80211_hw *hw, bool drop)
{
	struct wl_info *wl = HW_TO_WL(hw);

	no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false");

	/* wait for packet queue and dma fifos to run empty */
	WL_LOCK(wl);
	wlc_wait_for_tx_completion(wl->wlc, drop);
	WL_UNLOCK(wl);
}
Exemplo n.º 4
0
static void wl_ops_rfkill_poll(struct ieee80211_hw *hw)
{
	struct wl_info *wl = HW_TO_WL(hw);
	bool blocked;

	WL_LOCK(wl);
	blocked = wlc_check_radio_disabled(wl->wlc);
	WL_UNLOCK(wl);

	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
}
Exemplo n.º 5
0
static void
wl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
	struct wl_info *wl;

	wl = HW_TO_WL(hw);

	/* put driver in down state */
	WL_LOCK(wl);
	wl_down(wl);
	WL_UNLOCK(wl);
}
Exemplo n.º 6
0
static void
brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
	struct brcms_info *wl;

	wl = HW_TO_WL(hw);

	/* put driver in down state */
	LOCK(wl);
	brcms_down(wl);
	UNLOCK(wl);
}
Exemplo n.º 7
0
/*
 * is called in wl_pci_probe() context, therefore no locking required.
 */
static int ieee_hw_rate_init(struct ieee80211_hw *hw)
{
	struct wl_info *wl = HW_TO_WL(hw);
	int has_5g;
	char phy_list[4];

	has_5g = 0;

	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
	hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;

	if (wlc_get(wl->wlc, WLC_GET_PHYLIST, (int *)&phy_list) < 0) {
		WL_ERROR("Phy list failed\n");
	}
	WL_NONE("%s: phylist = %c\n", __func__, phy_list[0]);

	if (phy_list[0] == 'n' || phy_list[0] == 'c') {
		if (phy_list[0] == 'c') {
			/* Single stream */
			wl_band_2GHz_nphy.ht_cap.mcs.rx_mask[1] = 0;
			wl_band_2GHz_nphy.ht_cap.mcs.rx_highest = 72;
		}
		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl_band_2GHz_nphy;
	} else {
		BUG();
		return -1;
	}

	/* Assume all bands use the same phy.  True for 11n devices. */
	if (NBANDS_PUB(wl->pub) > 1) {
		has_5g++;
		if (phy_list[0] == 'n' || phy_list[0] == 'c') {
			hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
			    &wl_band_5GHz_nphy;
		} else {
			return -1;
		}
	}

	WL_NONE("%s: 2ghz = %d, 5ghz = %d\n", __func__, 1, has_5g);

	return 0;
}
Exemplo n.º 8
0
static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
{
	struct wl_info *wl;
	struct ieee80211_hw *hw;

	hw = pci_get_drvdata(pdev);
	wl = HW_TO_WL(hw);
	if (!wl) {
		wiphy_err(wl->wiphy,
			  "wl_suspend: pci_get_drvdata failed\n");
		return -ENODEV;
	}

	/* only need to flag hw is down for proper resume */
	WL_LOCK(wl);
	wl->pub->hw_up = false;
	WL_UNLOCK(wl);

	pci_save_state(pdev);
	pci_disable_device(pdev);
	return pci_set_power_state(pdev, PCI_D3hot);
}
Exemplo n.º 9
0
/*
 * precondition: perimeter lock has been acquired
 */
static int
ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
		 enum nl80211_channel_type type)
{
	struct wl_info *wl = HW_TO_WL(hw);
	int err = 0;

	switch (type) {
	case NL80211_CHAN_HT20:
	case NL80211_CHAN_NO_HT:
		err = wlc_set(wl->wlc, WLC_SET_CHANNEL, chan->hw_value);
		break;
	case NL80211_CHAN_HT40MINUS:
	case NL80211_CHAN_HT40PLUS:
		WL_ERROR("%s: Need to implement 40 Mhz Channels!\n", __func__);
		err = 1;
		break;
	}

	if (err)
		return -EIO;
	return err;
}
Exemplo n.º 10
0
static int wl_resume(struct pci_dev *pdev)
{
	struct wl_info *wl;
	struct ieee80211_hw *hw;
	int err = 0;
	u32 val;

	hw = pci_get_drvdata(pdev);
	wl = HW_TO_WL(hw);
	if (!wl) {
		wiphy_err(wl->wiphy,
			  "wl: wl_resume: pci_get_drvdata failed\n");
		return -ENODEV;
	}

	err = pci_set_power_state(pdev, PCI_D0);
	if (err)
		return err;

	pci_restore_state(pdev);

	err = pci_enable_device(pdev);
	if (err)
		return err;

	pci_set_master(pdev);

	pci_read_config_dword(pdev, 0x40, &val);
	if ((val & 0x0000ff00) != 0)
		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);

	/*
	*  done. driver will be put in up state
	*  in wl_ops_add_interface() call.
	*/
	return err;
}
Exemplo n.º 11
0
/*
 * is called in brcms_pci_probe() context, therefore no locking required.
 */
static int ieee_hw_rate_init(struct ieee80211_hw *hw)
{
	struct brcms_info *wl = HW_TO_WL(hw);
	int has_5g;
	char phy_list[4];

	has_5g = 0;

	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
	hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;

	if (brcms_c_get(wl->wlc, BRCM_GET_PHYLIST, (int *)&phy_list) < 0)
		wiphy_err(hw->wiphy, "Phy list failed\n");

	if (phy_list[0] == 'n' || phy_list[0] == 'c') {
		if (phy_list[0] == 'c') {
			/* Single stream */
			brcms_band_2GHz_nphy.ht_cap.mcs.rx_mask[1] = 0;
			brcms_band_2GHz_nphy.ht_cap.mcs.rx_highest = 72;
		}
		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &brcms_band_2GHz_nphy;
	} else {
		return -EPERM;
	}

	/* Assume all bands use the same phy.  True for 11n devices. */
	if (NBANDS_PUB(wl->pub) > 1) {
		has_5g++;
		if (phy_list[0] == 'n' || phy_list[0] == 'c') {
			hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
			    &brcms_band_5GHz_nphy;
		} else {
			return -EPERM;
		}
	}
	return 0;
}
Exemplo n.º 12
0
static void
wl_ops_bss_info_changed(struct ieee80211_hw *hw,
			struct ieee80211_vif *vif,
			struct ieee80211_bss_conf *info, u32 changed)
{
	struct wl_info *wl = HW_TO_WL(hw);
	struct wiphy *wiphy = hw->wiphy;
	int val;

	if (changed & BSS_CHANGED_ASSOC) {
		/* association status changed (associated/disassociated)
		 * also implies a change in the AID.
		 */
		wiphy_err(wiphy, "%s: %s: %sassociated\n", KBUILD_MODNAME,
			  __func__, info->assoc ? "" : "dis");
		WL_LOCK(wl);
		wlc_associate_upd(wl->wlc, info->assoc);
		WL_UNLOCK(wl);
	}
	if (changed & BSS_CHANGED_ERP_SLOT) {
		/* slot timing changed */
		if (info->use_short_slot)
			val = 1;
		else
			val = 0;
		WL_LOCK(wl);
		wlc_set(wl->wlc, WLC_SET_SHORTSLOT_OVERRIDE, val);
		WL_UNLOCK(wl);
	}

	if (changed & BSS_CHANGED_HT) {
		/* 802.11n parameters changed */
		u16 mode = info->ht_operation_mode;

		WL_LOCK(wl);
		wlc_protection_upd(wl->wlc, WLC_PROT_N_CFG,
			mode & IEEE80211_HT_OP_MODE_PROTECTION);
		wlc_protection_upd(wl->wlc, WLC_PROT_N_NONGF,
			mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
		wlc_protection_upd(wl->wlc, WLC_PROT_N_OBSS,
			mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
		WL_UNLOCK(wl);
	}
	if (changed & BSS_CHANGED_BASIC_RATES) {
		struct ieee80211_supported_band *bi;
		u32 br_mask, i;
		u16 rate;
		struct wl_rateset rs;
		int error;

		/* retrieve the current rates */
		WL_LOCK(wl);
		error = wlc_ioctl(wl->wlc, WLC_GET_CURR_RATESET,
				  &rs, sizeof(rs), NULL);
		WL_UNLOCK(wl);
		if (error) {
			wiphy_err(wiphy, "%s: retrieve rateset failed: %d\n",
				  __func__, error);
			return;
		}
		br_mask = info->basic_rates;
		bi = hw->wiphy->bands[wlc_get_curband(wl->wlc)];
		for (i = 0; i < bi->n_bitrates; i++) {
			/* convert to internal rate value */
			rate = (bi->bitrates[i].bitrate << 1) / 10;

			/* set/clear basic rate flag */
			wl_set_basic_rate(&rs, rate, br_mask & 1);
			br_mask >>= 1;
		}

		/* update the rate set */
		WL_LOCK(wl);
		wlc_ioctl(wl->wlc, WLC_SET_RATESET, &rs, sizeof(rs), NULL);
		WL_UNLOCK(wl);
	}
	if (changed & BSS_CHANGED_BEACON_INT) {
		/* Beacon interval changed */
		WL_LOCK(wl);
		wlc_set(wl->wlc, WLC_SET_BCNPRD, info->beacon_int);
		WL_UNLOCK(wl);
	}
	if (changed & BSS_CHANGED_BSSID) {
		/* BSSID changed, for whatever reason (IBSS and managed mode) */
		WL_LOCK(wl);
		wlc_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
				  info->bssid);
		WL_UNLOCK(wl);
	}
	if (changed & BSS_CHANGED_BEACON) {
		/* Beacon data changed, retrieve new beacon (beaconing modes) */
		wiphy_err(wiphy, "%s: beacon changed\n", __func__);
	}
	if (changed & BSS_CHANGED_BEACON_ENABLED) {
		/* Beaconing should be enabled/disabled (beaconing modes) */
		wiphy_err(wiphy, "%s: Beacon enabled: %s\n", __func__,
			  info->enable_beacon ? "true" : "false");
	}
	if (changed & BSS_CHANGED_CQM) {
		/* Connection quality monitor config changed */
		wiphy_err(wiphy, "%s: cqm change: threshold %d, hys %d "
			  " (implement)\n", __func__, info->cqm_rssi_thold,
			  info->cqm_rssi_hyst);
	}
	if (changed & BSS_CHANGED_IBSS) {
		/* IBSS join status changed */
		wiphy_err(wiphy, "%s: IBSS joined: %s (implement)\n", __func__,
			  info->ibss_joined ? "true" : "false");
	}
	if (changed & BSS_CHANGED_ARP_FILTER) {
		/* Hardware ARP filter address list or state changed */
		wiphy_err(wiphy, "%s: arp filtering: enabled %s, count %d"
			  " (implement)\n", __func__, info->arp_filter_enabled ?
			  "true" : "false", info->arp_addr_cnt);
	}
	if (changed & BSS_CHANGED_QOS) {
		/*
		 * QoS for this association was enabled/disabled.
		 * Note that it is only ever disabled for station mode.
		 */
		wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__,
			  info->qos ? "true" : "false");
	}
	return;
}
Exemplo n.º 13
0
static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
{
	struct ieee80211_conf *conf = &hw->conf;
	struct wl_info *wl = HW_TO_WL(hw);
	int err = 0;
	int new_int;
	struct wiphy *wiphy = hw->wiphy;

	WL_LOCK(wl);
	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
		if (wlc_iovar_setint
		    (wl->wlc, "bcn_li_bcn", conf->listen_interval)) {
			wiphy_err(wiphy, "%s: Error setting listen_interval\n",
				  __func__);
			err = -EIO;
			goto config_out;
		}
		wlc_iovar_getint(wl->wlc, "bcn_li_bcn", &new_int);
	}
	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
		wiphy_err(wiphy, "%s: change monitor mode: %s (implement)\n",
			  __func__, conf->flags & IEEE80211_CONF_MONITOR ?
			  "true" : "false");
	if (changed & IEEE80211_CONF_CHANGE_PS)
		wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n",
			  __func__, conf->flags & IEEE80211_CONF_PS ?
			  "true" : "false");

	if (changed & IEEE80211_CONF_CHANGE_POWER) {
		if (wlc_iovar_setint
		    (wl->wlc, "qtxpower", conf->power_level * 4)) {
			wiphy_err(wiphy, "%s: Error setting power_level\n",
				  __func__);
			err = -EIO;
			goto config_out;
		}
		wlc_iovar_getint(wl->wlc, "qtxpower", &new_int);
		if (new_int != (conf->power_level * 4))
			wiphy_err(wiphy, "%s: Power level req != actual, %d %d"
				  "\n", __func__, conf->power_level * 4,
				  new_int);
	}
	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
		err = ieee_set_channel(hw, conf->channel, conf->channel_type);
	}
	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
		if (wlc_set
		    (wl->wlc, WLC_SET_SRL,
		     conf->short_frame_max_tx_count) < 0) {
			wiphy_err(wiphy, "%s: Error setting srl\n", __func__);
			err = -EIO;
			goto config_out;
		}
		if (wlc_set(wl->wlc, WLC_SET_LRL, conf->long_frame_max_tx_count)
		    < 0) {
			wiphy_err(wiphy, "%s: Error setting lrl\n", __func__);
			err = -EIO;
			goto config_out;
		}
	}

 config_out:
	WL_UNLOCK(wl);
	return err;
}
Exemplo n.º 14
0
static void
wl_ops_bss_info_changed(struct ieee80211_hw *hw,
			struct ieee80211_vif *vif,
			struct ieee80211_bss_conf *info, u32 changed)
{
	struct wl_info *wl = HW_TO_WL(hw);
	int val;

	if (changed & BSS_CHANGED_ASSOC) {
		/* association status changed (associated/disassociated)
		 * also implies a change in the AID.
		 */
		WL_ERROR("%s: %s: %sassociated\n", KBUILD_MODNAME, __func__,
			 info->assoc ? "" : "dis");
		wlc_associate_upd(wl->wlc, info->assoc);
	}
	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
		/* CTS protection changed */
		WL_ERROR("%s: use_cts_prot: %s (implement)\n", __func__,
			info->use_cts_prot ? "true" : "false");
	}
	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
		/* preamble changed */
		WL_ERROR("%s: short preamble: %s (implement)\n", __func__,
			info->use_short_preamble ? "true" : "false");
	}
	if (changed & BSS_CHANGED_ERP_SLOT) {
		/* slot timing changed */
		if (info->use_short_slot)
			val = 1;
		else
			val = 0;
		WL_LOCK(wl);
		wlc_set(wl->wlc, WLC_SET_SHORTSLOT_OVERRIDE, val);
		WL_UNLOCK(wl);
	}

	if (changed & BSS_CHANGED_HT) {
		/* 802.11n parameters changed */
		u16 mode = info->ht_operation_mode;
		WL_NONE("%s: HT mode: 0x%04X\n", __func__, mode);
		wlc_protection_upd(wl->wlc, WLC_PROT_N_CFG,
			mode & IEEE80211_HT_OP_MODE_PROTECTION);
		wlc_protection_upd(wl->wlc, WLC_PROT_N_NONGF,
			mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
		wlc_protection_upd(wl->wlc, WLC_PROT_N_OBSS,
			mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
	}
	if (changed & BSS_CHANGED_BASIC_RATES) {
		/* Basic rateset changed */
		WL_ERROR("%s: Need to change Basic Rates: 0x%x (implement)\n",
			 __func__, (u32) info->basic_rates);
	}
	if (changed & BSS_CHANGED_BEACON_INT) {
		/* Beacon interval changed */
		WL_NONE("%s: Beacon Interval: %d\n",
			__func__, info->beacon_int);
		wlc_set(wl->wlc, WLC_SET_BCNPRD, info->beacon_int);
	}
	if (changed & BSS_CHANGED_BSSID) {
		/* BSSID changed, for whatever reason (IBSS and managed mode) */
		WL_NONE("%s: new BSSID: aid %d  bss:%pM\n", __func__,
			info->aid, info->bssid);
		WL_LOCK(wl);
		wlc_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
				  info->bssid);
		WL_UNLOCK(wl);
	}
	if (changed & BSS_CHANGED_BEACON) {
		/* Beacon data changed, retrieve new beacon (beaconing modes) */
		WL_ERROR("%s: beacon changed\n", __func__);
	}
	if (changed & BSS_CHANGED_BEACON_ENABLED) {
		/* Beaconing should be enabled/disabled (beaconing modes) */
		WL_ERROR("%s: Beacon enabled: %s\n", __func__,
			 info->enable_beacon ? "true" : "false");
	}
	if (changed & BSS_CHANGED_CQM) {
		/* Connection quality monitor config changed */
		WL_ERROR("%s: cqm change: threshold %d, hys %d (implement)\n",
			__func__, info->cqm_rssi_thold, info->cqm_rssi_hyst);
	}
	if (changed & BSS_CHANGED_IBSS) {
		/* IBSS join status changed */
		WL_ERROR("%s: IBSS joined: %s (implement)\n", __func__,
			info->ibss_joined ? "true" : "false");
	}
	if (changed & BSS_CHANGED_ARP_FILTER) {
		/* Hardware ARP filter address list or state changed */
		WL_ERROR("%s: arp filtering: enabled %s, count %d (implement)\n",
			__func__, info->arp_filter_enabled ? "true" : "false",
			info->arp_addr_cnt);
	}
	if (changed & BSS_CHANGED_QOS) {
		/*
		 * QoS for this association was enabled/disabled.
		 * Note that it is only ever disabled for station mode.
		 */
		WL_ERROR("%s: qos enabled: %s (implement)\n", __func__,
			info->qos ? "true" : "false");
	}
	if (changed & BSS_CHANGED_IDLE) {
		/* Idle changed for this BSS/interface */
		WL_ERROR("%s: BSS idle: %s (implement)\n", __func__,
			info->idle ? "true" : "false");
	}
	return;
}