/* * 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); }
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; }
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); }
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); }
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); }
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); }
/* * 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; }
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); }
/* * 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; }
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; }
/* * 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; }
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; }
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; }
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; }