int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); ret = iwl_trans_start_hw(mvm->trans); if (ret) return ret; /* * If we haven't completed the run of the init ucode during * module loading, load init ucode now * (for example, if we were in RFKILL) */ if (!mvm->init_ucode_complete) { ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); /* this can't happen */ if (WARN_ON(ret > 0)) ret = -ERFKILL; goto error; } if (!iwlmvm_mod_params.init_dbg) { /* * should stop and start HW since that INIT * image just loaded */ iwl_trans_stop_device(mvm->trans); ret = iwl_trans_start_hw(mvm->trans); if (ret) return ret; } } if (iwlmvm_mod_params.init_dbg) return 0; ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); goto error; } ret = iwl_mvm_sf_update(mvm, NULL, false); if (ret) IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); if (ret) goto error; ret = iwl_send_bt_init_conf(mvm); if (ret) goto error; /* Send phy db control command and then phy db calibration*/ ret = iwl_send_phy_db_data(mvm->phy_db); if (ret) goto error; ret = iwl_send_phy_cfg_cmd(mvm); if (ret) goto error; /* init the fw <-> mac80211 STA mapping */ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); /* Add auxiliary station for scanning */ ret = iwl_mvm_add_aux_sta(mvm); if (ret) goto error; /* Add all the PHY contexts */ chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); for (i = 0; i < NUM_PHY_CTX; i++) { /* * The channel used here isn't relevant as it's * going to be overwritten in the other flows. * For now use the first channel we have. */ ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], &chandef, 1, 1); if (ret) goto error; } /* Initialize tx backoffs to the minimal possible */ iwl_mvm_tt_tx_backoff(mvm, 0); ret = iwl_mvm_power_update_device(mvm); if (ret) goto error; /* allow FW/transport low power modes if not during restart */ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: iwl_trans_stop_device(mvm->trans); return ret; }
int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); ret = iwl_trans_start_hw(mvm->trans); if (ret) return ret; ret = iwl_mvm_load_rt_fw(mvm); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); goto error; } iwl_get_shared_mem_conf(&mvm->fwrt); ret = iwl_mvm_sf_update(mvm, NULL, false); if (ret) IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); #ifdef CPTCFG_IWLWIFI_DEVICE_TESTMODE iwl_dnt_start(mvm->trans); #endif mvm->fwrt.dump.conf = FW_DBG_INVALID; /* if we have a destination, assume EARLY START */ if (mvm->fw->dbg.dest_tlv) mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE; iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE); #ifdef CPTCFG_MAC80211_LATENCY_MEASUREMENTS if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TX_LATENCY)) { struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_tx_latency *thrshold_trig; u32 thrshld; u32 vif; u32 iface = 0; u16 tid; u16 mode; u32 window; trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TX_LATENCY); vif = le32_to_cpu(trig->vif_type); if (vif == IWL_FW_DBG_CONF_VIF_ANY) { iface = BIT(IEEE80211_TX_LATENCY_BSS); iface |= BIT(IEEE80211_TX_LATENCY_P2P); } else if (vif <= IWL_FW_DBG_CONF_VIF_AP) { iface = BIT(IEEE80211_TX_LATENCY_BSS); } else { iface = BIT(IEEE80211_TX_LATENCY_P2P); } thrshold_trig = (void *)trig->data; thrshld = le32_to_cpu(thrshold_trig->thrshold); tid = le16_to_cpu(thrshold_trig->tid_bitmap); mode = le16_to_cpu(thrshold_trig->mode); window = le32_to_cpu(thrshold_trig->window); IWL_DEBUG_INFO(mvm, "Tx latency trigger cfg: threshold = %u, tid = 0x%x, mode = 0x%x, window = %u vif = 0x%x\n", thrshld, tid, mode, window, iface); ieee80211_tx_lat_thrshld_cfg(mvm->hw, thrshld, tid, window, mode, iface); } #endif ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; if (!iwl_mvm_has_unified_ucode(mvm)) { /* Send phy db control command and then phy db calibration */ ret = iwl_send_phy_db_data(mvm->phy_db); if (ret) goto error; ret = iwl_send_phy_cfg_cmd(mvm); if (ret) goto error; } ret = iwl_mvm_send_bt_init_conf(mvm); if (ret) goto error; if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SOC_LATENCY_SUPPORT)) { ret = iwl_set_soc_latency(mvm); if (ret) goto error; } /* Init RSS configuration */ if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) { ret = iwl_configure_rxq(mvm); if (ret) { IWL_ERR(mvm, "Failed to configure RX queues: %d\n", ret); goto error; } } if (iwl_mvm_has_new_rx_api(mvm)) { ret = iwl_send_rss_cfg_cmd(mvm); if (ret) { IWL_ERR(mvm, "Failed to configure RSS queues: %d\n", ret); goto error; } } /* init the fw <-> mac80211 STA mapping */ for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; /* reset quota debouncing buffer - 0xff will yield invalid data */ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); ret = iwl_mvm_send_dqa_cmd(mvm); if (ret) goto error; /* Add auxiliary station for scanning */ ret = iwl_mvm_add_aux_sta(mvm); if (ret) goto error; /* Add all the PHY contexts */ chan = &mvm->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[0]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); for (i = 0; i < NUM_PHY_CTX; i++) { /* * The channel used here isn't relevant as it's * going to be overwritten in the other flows. * For now use the first channel we have. */ ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], &chandef, 1, 1); if (ret) goto error; } #ifdef CONFIG_THERMAL if (iwl_mvm_is_tt_in_fw(mvm)) { /* in order to give the responsibility of ct-kill and * TX backoff to FW we need to send empty temperature reporting * cmd during init time */ iwl_mvm_send_temp_report_ths_cmd(mvm); } else { /* Initialize tx backoffs to the minimal possible */ iwl_mvm_tt_tx_backoff(mvm, 0); } /* TODO: read the budget from BIOS / Platform NVM */ /* * In case there is no budget from BIOS / Platform NVM the default * budget should be 2000mW (cooling state 0). */ if (iwl_mvm_is_ctdp_supported(mvm)) { ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, mvm->cooling_dev.cur_state); if (ret) goto error; } #else /* Initialize tx backoffs to the minimal possible */ iwl_mvm_tt_tx_backoff(mvm, 0); #endif WARN_ON(iwl_mvm_config_ltr(mvm)); ret = iwl_mvm_power_update_device(mvm); if (ret) goto error; /* * RTNL is not taken during Ct-kill, but we don't need to scan/Tx * anyway, so don't init MCC. */ if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) { ret = iwl_mvm_init_mcc(mvm); if (ret) goto error; } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; mvm->hb_scan_type = IWL_SCAN_TYPE_NOT_SET; ret = iwl_mvm_config_scan(mvm); if (ret) goto error; } /* allow FW/transport low power modes if not during restart */ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); #ifdef CPTCFG_IWLWIFI_LTE_COEX iwl_mvm_send_lte_commands(mvm); #endif #ifdef CPTCFG_IWLMVM_VENDOR_CMDS /* set_mode must be IWL_TX_POWER_MODE_SET_DEVICE if this was * ever initialized. */ if (le32_to_cpu(mvm->txp_cmd.v5.v3.set_mode) == IWL_TX_POWER_MODE_SET_DEVICE) { int len; if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_REDUCE_TX_POWER)) len = sizeof(mvm->txp_cmd.v5); else if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) len = sizeof(mvm->txp_cmd.v4); else len = sizeof(mvm->txp_cmd.v4.v3); if (iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &mvm->txp_cmd)) IWL_ERR(mvm, "failed to update TX power\n"); } #endif #ifdef CPTCFG_IWLWIFI_FRQ_MGR iwl_mvm_fm_notify_current_dcdc(); #endif ret = iwl_mvm_sar_init(mvm); if (ret) goto error; ret = iwl_mvm_sar_geo_init(mvm); if (ret) goto error; iwl_mvm_leds_sync(mvm); IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: if (!iwlmvm_mod_params.init_dbg || !ret) iwl_mvm_stop_device(mvm); return ret; }
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; /* * Not much to do here. The stack will not allow interface * types or combinations that we didn't advertise, so we * don't really have to check the types. */ mutex_lock(&mvm->mutex); /* Allocate resources for the MAC context, and add it the the fw */ ret = iwl_mvm_mac_ctxt_init(mvm, vif); if (ret) goto out_unlock; /* * The AP binding flow can be done only after the beacon * template is configured (which happens only in the mac80211 * start_ap() flow), and adding the broadcast station can happen * only after the binding. * In addition, since modifying the MAC before adding a bcast * station is not allowed by the FW, delay the adding of MAC context to * the point where we can also add the bcast station. * In short: there's not much we can do at this point, other than * allocating resources :) */ if (vif->type == NL80211_IFTYPE_AP) { u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask); if (ret) { IWL_ERR(mvm, "Failed to allocate bcast sta\n"); goto out_release; } goto out_unlock; } /* * TODO: remove this temporary code. * Currently MVM FW supports power management only on single MAC. * If new interface added, disable PM on existing interface. * P2P device is a special case, since it is handled by FW similary to * scan. If P2P deviced is added, PM remains enabled on existing * interface. * Note: the method below does not count the new interface being added * at this moment. */ if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count++; if (mvm->vif_count > 1) { IWL_DEBUG_MAC80211(mvm, "Disable power on existing interfaces\n"); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_pm_disable_iterator, mvm); } ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) goto out_release; /* * Update power state on the new interface. Admittedly, based on * mac80211 logics this power update will disable power management */ iwl_mvm_power_update_mode(mvm, vif); /* * P2P_DEVICE interface does not have a channel context assigned to it, * so a dedicated PHY context is allocated to it and the corresponding * MAC context is bound to it at this stage. */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; mvmvif->phy_ctxt = &mvm->phy_ctxt_roc; /* * The channel used here isn't relevant as it's * going to be overwritten as part of the ROC flow. * For now use the first channel we have. */ chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef, 1, 1); if (ret) goto out_remove_mac; ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) goto out_remove_phy; ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) goto out_unbind; /* Save a pointer to p2p device vif, so it can later be used to * update the p2p device MAC when a GO is started/stopped */ mvm->p2p_device_vif = vif; } iwl_mvm_vif_dbgfs_register(mvm, vif); goto out_unlock; out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); out_remove_phy: iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); out_remove_mac: mvmvif->phy_ctxt = NULL; iwl_mvm_mac_ctxt_remove(mvm, vif); out_release: /* * TODO: remove this temporary code. * Currently MVM FW supports power management only on single MAC. * Check if only one additional interface remains after releasing * current one. Update power mode on the remaining interface. */ if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", mvm->vif_count); if (mvm->vif_count == 1) { ieee80211_iterate_active_interfaces( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_power_update_iterator, mvm); } iwl_mvm_mac_ctxt_release(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); return ret; }
static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mcast_filter_cmd mcast_filter_cmd = { .pass_all = 1, }; memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN); return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, sizeof(mcast_filter_cmd), &mcast_filter_cmd); } static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; ret = iwl_mvm_mac_ctxt_changed(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { /* add quota for this interface */ ret = iwl_mvm_update_quotas(mvm, vif); if (ret) { IWL_ERR(mvm, "failed to update quotas\n"); return; } iwl_mvm_bt_coex_vif_assoc(mvm, vif); iwl_mvm_configure_mcast_filter(mvm, vif); } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { /* remove AP station now that the MAC is unassoc */ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); if (ret) IWL_ERR(mvm, "failed to remove AP station\n"); mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; /* remove quota for this interface */ ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) IWL_ERR(mvm, "failed to update quotas\n"); } } else if (changes & BSS_CHANGED_DTIM_PERIOD) { /* * We received a beacon _after_ association so * remove the session protection. */ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); } else if (changes & BSS_CHANGED_PS) { /* * TODO: remove this temporary code. * Currently MVM FW supports power management only on single * MAC. Avoid power mode update if more than one interface * is active. */ IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", mvm->vif_count); if (mvm->vif_count == 1) { ret = iwl_mvm_power_update_mode(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } } } static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; mutex_lock(&mvm->mutex); /* Send the beacon template */ ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif); if (ret) goto out_unlock; /* Add the mac context */ ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) goto out_unlock; /* Perform the binding */ ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) goto out_remove; mvmvif->ap_active = true; /* Send the bcast station. At this stage the TBTT and DTIM time events * are added and applied to the scheduler */ ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) goto out_unbind; ret = iwl_mvm_update_quotas(mvm, vif); if (ret) goto out_rm_bcast; /* Need to update the P2P Device MAC */ if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); mutex_unlock(&mvm->mutex); return 0; out_rm_bcast: iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); out_remove: iwl_mvm_mac_ctxt_remove(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); return ret; } static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); iwl_mvm_prepare_mac_removal(mvm, vif); mutex_lock(&mvm->mutex); mvmvif->ap_active = false; /* Need to update the P2P Device MAC */ if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_mac_ctxt_remove(mvm, vif); mutex_unlock(&mvm->mutex); } static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { /* Need to send a new beacon template to the FW */ if (changes & BSS_CHANGED_BEACON) { if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) IWL_WARN(mvm, "Failed updating beacon data\n"); } } static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mutex_lock(&mvm->mutex); switch (vif->type) { case NL80211_IFTYPE_STATION: iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); break; case NL80211_IFTYPE_AP: iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes); break; default: /* shouldn't happen */ WARN_ON_ONCE(1); } mutex_unlock(&mvm->mutex); } static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS) return -EINVAL; mutex_lock(&mvm->mutex); if (mvm->scan_status == IWL_MVM_SCAN_NONE) ret = iwl_mvm_scan_request(mvm, vif, req); else ret = -EBUSY; mutex_unlock(&mvm->mutex); return ret; } static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mutex_lock(&mvm->mutex); iwl_mvm_cancel_scan(mvm); mutex_unlock(&mvm->mutex); } static void iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid, int num_frames, enum ieee80211_frame_release_type reason, bool more_data) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); /* TODO: how do we tell the fw to send frames for a specific TID */ /* * The fw will send EOSP notification when the last frame will be * transmitted. */ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames); } static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; switch (cmd) { case STA_NOTIFY_SLEEP: if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) ieee80211_sta_block_awake(hw, sta, true); /* * The fw updates the STA to be asleep. Tx packets on the Tx * queues to this station will not be transmitted. The fw will * send a Tx response with TX_STATUS_FAIL_DEST_PS. */ break; case STA_NOTIFY_AWAKE: if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) break; iwl_mvm_sta_modify_ps_wake(mvm, sta); break; default: break; } } static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n", sta->addr, old_state, new_state); /* this would be a mac80211 bug ... but don't crash */ if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; /* if a STA is being removed, reuse its ID */ flush_work(&mvm->sta_drained_wk); mutex_lock(&mvm->mutex); if (old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) { ret = iwl_mvm_add_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { ret = iwl_mvm_update_sta(mvm, vif, sta); if (ret == 0) iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_NONE) { ret = 0; } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) { ret = iwl_mvm_rm_sta(mvm, vif, sta); } else { ret = -EIO; } mutex_unlock(&mvm->mutex); return ret; } static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mvm->rts_threshold = value; return 0; } static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); mvmvif->queue_params[ac] = *params; /* * No need to update right away, we'll get BSS_CHANGED_QOS * The exception is P2P_DEVICE interface which needs immediate update. */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { int ret; mutex_lock(&mvm->mutex); ret = iwl_mvm_mac_ctxt_changed(mvm, vif); mutex_unlock(&mvm->mutex); return ret; } return 0; } static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS, 200 + vif->bss_conf.beacon_int); u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS, 100 + vif->bss_conf.beacon_int); if (WARN_ON_ONCE(vif->bss_conf.assoc)) return; mutex_lock(&mvm->mutex); /* Try really hard to protect the session and hear a beacon */ iwl_mvm_protect_session(mvm, vif, duration, min_duration); mutex_unlock(&mvm->mutex); } static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; if (iwlwifi_mod_params.sw_crypto) { IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n"); return -EOPNOTSUPP; } switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; /* fall-through */ case WLAN_CIPHER_SUITE_CCMP: key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; case WLAN_CIPHER_SUITE_AES_CMAC: WARN_ON_ONCE(!(hw->flags & IEEE80211_HW_MFP_CAPABLE)); break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: /* * Support for TX only, at least for now, so accept * the key and do nothing else. Then mac80211 will * pass it for TX but we don't have to use it for RX. */ return 0; default: return -EOPNOTSUPP; } mutex_lock(&mvm->mutex); switch (cmd) { case SET_KEY: if (vif->type == NL80211_IFTYPE_AP && !sta) { /* GTK on AP interface is a TX-only key, return 0 */ ret = 0; key->hw_key_idx = STA_KEY_IDX_INVALID; break; } IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n"); ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false); if (ret) { IWL_WARN(mvm, "set key failed\n"); /* * can't add key for RX, but we don't need it * in the device for TX so still return 0 */ key->hw_key_idx = STA_KEY_IDX_INVALID; ret = 0; } break; case DISABLE_KEY: if (key->hw_key_idx == STA_KEY_IDX_INVALID) { ret = 0; break; } IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n"); ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); break; default: ret = -EINVAL; } mutex_unlock(&mvm->mutex); return ret; } static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *keyconf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key); } static int iwl_mvm_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, int duration, enum ieee80211_roc_type type) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct cfg80211_chan_def chandef; int ret; if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type); return -EINVAL; } IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, duration, type); mutex_lock(&mvm->mutex); cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc, &chandef, 1, 1); /* Schedule the time events */ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); mutex_unlock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "leave\n"); return ret; } static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); IWL_DEBUG_MAC80211(mvm, "enter\n"); mutex_lock(&mvm->mutex); iwl_mvm_stop_p2p_roc(mvm); mutex_unlock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "leave\n"); return 0; } static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; int ret; mutex_lock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "Add PHY context\n"); ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def, ctx->rx_chains_static, ctx->rx_chains_dynamic); mutex_unlock(&mvm->mutex); return ret; } static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; mutex_lock(&mvm->mutex); iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt); mutex_unlock(&mvm->mutex); } static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; mutex_lock(&mvm->mutex); iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, ctx->rx_chains_static, ctx->rx_chains_dynamic); mutex_unlock(&mvm->mutex); } static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; mutex_lock(&mvm->mutex); mvmvif->phy_ctxt = phyctx; switch (vif->type) { case NL80211_IFTYPE_AP: /* * The AP binding flow is handled as part of the start_ap flow * (in bss_info_changed). */ ret = 0; goto out_unlock; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MONITOR: break; default: ret = -EINVAL; goto out_unlock; } ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) goto out_unlock; /* * Setting the quota at this stage is only required for monitor * interfaces. For the other types, the bss_info changed flow * will handle quota settings. */ if (vif->type == NL80211_IFTYPE_MONITOR) { mvmvif->monitor_active = true; ret = iwl_mvm_update_quotas(mvm, vif); if (ret) goto out_remove_binding; } goto out_unlock; out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); if (ret) mvmvif->phy_ctxt = NULL; return ret; } static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); mutex_lock(&mvm->mutex); iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); if (vif->type == NL80211_IFTYPE_AP) goto out_unlock; switch (vif->type) { case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; iwl_mvm_update_quotas(mvm, NULL); break; default: break; } iwl_mvm_binding_remove_vif(mvm, vif); out_unlock: mvmvif->phy_ctxt = NULL; mutex_unlock(&mvm->mutex); } static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; if (!mvm_sta || !mvm_sta->vif) { IWL_ERR(mvm, "Station is not associated to a vif\n"); return -EINVAL; } return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); } static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); iwl_mvm_bt_rssi_event(mvm, vif, rssi_event); } struct ieee80211_ops iwl_mvm_hw_ops = { .tx = iwl_mvm_mac_tx, .ampdu_action = iwl_mvm_mac_ampdu_action, .start = iwl_mvm_mac_start, .restart_complete = iwl_mvm_mac_restart_complete, .stop = iwl_mvm_mac_stop, .add_interface = iwl_mvm_mac_add_interface, .remove_interface = iwl_mvm_mac_remove_interface, .config = iwl_mvm_mac_config, .configure_filter = iwl_mvm_configure_filter, .bss_info_changed = iwl_mvm_bss_info_changed, .hw_scan = iwl_mvm_mac_hw_scan, .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, .sta_state = iwl_mvm_mac_sta_state, .sta_notify = iwl_mvm_mac_sta_notify, .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, .set_key = iwl_mvm_mac_set_key, .update_tkip_key = iwl_mvm_mac_update_tkip_key, .remain_on_channel = iwl_mvm_roc, .cancel_remain_on_channel = iwl_mvm_cancel_roc, .rssi_callback = iwl_mvm_mac_rssi_callback, .add_chanctx = iwl_mvm_add_chanctx, .remove_chanctx = iwl_mvm_remove_chanctx, .change_chanctx = iwl_mvm_change_chanctx, .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, .start_ap = iwl_mvm_start_ap, .stop_ap = iwl_mvm_stop_ap, .set_tim = iwl_mvm_set_tim, #ifdef CONFIG_PM_SLEEP /* look at d3.c */ .suspend = iwl_mvm_suspend, .resume = iwl_mvm_resume, .set_wakeup = iwl_mvm_set_wakeup, .set_rekey_data = iwl_mvm_set_rekey_data, #if IS_ENABLED(CONFIG_IPV6) .ipv6_addr_change = iwl_mvm_ipv6_addr_change, #endif .set_default_unicast_key = iwl_mvm_set_default_unicast_key, #endif };
int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); ret = iwl_trans_start_hw(mvm->trans); if (ret) return ret; /* If we were in RFKILL during module loading, load init ucode now */ if (!mvm->init_ucode_run) { ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); /* this can't happen */ if (WARN_ON(ret > 0)) ret = -ERFKILL; goto error; } /* should stop & start HW since that INIT image just loaded */ iwl_trans_stop_hw(mvm->trans, false); ret = iwl_trans_start_hw(mvm->trans); if (ret) return ret; } if (iwlmvm_mod_params.init_dbg) return 0; ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); goto error; } ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); if (ret) goto error; ret = iwl_send_bt_prio_tbl(mvm); if (ret) goto error; ret = iwl_send_bt_init_conf(mvm); if (ret) goto error; /* Send phy db control command and then phy db calibration*/ ret = iwl_send_phy_db_data(mvm->phy_db); if (ret) goto error; ret = iwl_send_phy_cfg_cmd(mvm); if (ret) goto error; /* init the fw <-> mac80211 STA mapping */ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); /* Add auxiliary station for scanning */ ret = iwl_mvm_add_aux_sta(mvm); if (ret) goto error; /* Add all the PHY contexts */ chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); for (i = 0; i < NUM_PHY_CTX; i++) { /* * The channel used here isn't relevant as it's * going to be overwritten in the other flows. * For now use the first channel we have. */ ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], &chandef, 1, 1); if (ret) goto error; } IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: iwl_trans_stop_device(mvm->trans); return ret; }