/* Maps the driver specific channel width definition to the fw values */
u8 iwl_mvm_get_channel_width(conststruct cfg80211_chan_def *chandef)
{ switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: return IWL_PHY_CHANNEL_MODE20; case NL80211_CHAN_WIDTH_40: return IWL_PHY_CHANNEL_MODE40; case NL80211_CHAN_WIDTH_80: return IWL_PHY_CHANNEL_MODE80; case NL80211_CHAN_WIDTH_160: return IWL_PHY_CHANNEL_MODE160; case NL80211_CHAN_WIDTH_320: return IWL_PHY_CHANNEL_MODE320; default:
WARN(1, "Invalid channel width=%u", chandef->width); return IWL_PHY_CHANNEL_MODE20;
}
}
/* * Maps the driver specific control channel position (relative to the center * freq) definitions to the fw values
*/
u8 iwl_mvm_get_ctrl_pos(conststruct cfg80211_chan_def *chandef)
{ int offs = chandef->chan->center_freq - chandef->center_freq1; int abs_offs = abs(offs);
u8 ret;
if (offs == 0) { /* * The FW is expected to check the control channel position only * when in HT/VHT and the channel width is not 20MHz. Return * this value as the default one.
*/ return 0;
}
/* this results in a value 0-7, i.e. fitting into 0b0111 */
ret = (abs_offs - 10) / 20; /* * But we need the value to be in 0b1011 because 0b0100 is * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000)
*/
ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) |
((ret & BIT(2)) << 1); /* and add the above bit */
ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE;
return ret;
}
/* * Construct the generic fields of the PHY context command
*/ staticvoid iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt, struct iwl_phy_context_cmd *cmd,
u32 action)
{
cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
ctxt->color));
cmd->action = cpu_to_le32(action);
}
/* Set rx the chains */
idle_cnt = chains_static;
active_cnt = chains_dynamic;
/* In scenarios where we only ever use a single-stream rates, * i.e. legacy 11b/g/a associations, single-stream APs or even * static SMPS, enable both chains to get diversity, improving * the case where we're far enough from the AP that attenuation * between the two antennas is sufficiently different to impact * performance.
*/ if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm, ctxt)) {
idle_cnt = 2;
active_cnt = 2;
}
/* Set the channel info data */
iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
/* we only support RLC command version 2 */ if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2)
iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd->rxchain_info,
chains_static, chains_dynamic);
}
/* From version 3, RLC is offloaded to firmware, so the driver no * longer needs to send cmd.rlc, note that we are not using any * other fields in the command - don't send it.
*/ if (iwl_mvm_has_rlc_offload(mvm) || ctxt->rlc_disabled) return 0;
if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP,
RLC_CONFIG_CMD), 0) < 2) return 0;
/* * Send a command to apply the current phy configuration. The command is send * only if something in the configuration changed: in case that this is the * first time that the phy configuration is applied or in case that the phy * configuration changed from the previous apply.
*/ staticint iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, conststruct cfg80211_chan_def *chandef, conststruct cfg80211_chan_def *ap,
u8 chains_static, u8 chains_dynamic,
u32 action)
{ int ret; int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1);
if (ver < 5 || !ap || !ap->chan)
ap = NULL;
if (ver >= 3 && ver <= 6) { struct iwl_phy_context_cmd cmd = {};
/* Set the command header fields */
iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action);
/* Set the command data */
iwl_mvm_phy_ctxt_cmd_data(mvm, ctxt, &cmd, chandef,
chains_static,
chains_dynamic);
if (ap) {
cmd.sbb_bandwidth = iwl_mvm_get_channel_width(ap);
cmd.sbb_ctrl_channel_loc = iwl_mvm_get_ctrl_pos(ap);
}
if (ver == 6)
cmd.puncture_mask = cpu_to_le16(chandef->punctured);
if (action != FW_CTXT_ACTION_REMOVE) return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
chains_dynamic);
return 0;
}
/* * Send a command to add a PHY context based on the current HW configuration.
*/ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, conststruct cfg80211_chan_def *chandef, conststruct cfg80211_chan_def *ap,
u8 chains_static, u8 chains_dynamic)
{ int ret;
ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, ap,
chains_static, chains_dynamic,
FW_CTXT_ACTION_ADD);
if (ret) return ret;
ctxt->ref++;
return 0;
}
/* * Update the number of references to the given PHY context. This is valid only * in case the PHY context was already created, i.e., its reference count > 0.
*/ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
{
lockdep_assert_held(&mvm->mutex);
/* If we were taking the first ref, we should have * called iwl_mvm_phy_ctxt_add.
*/
WARN_ON(!ctxt->ref);
ctxt->ref++;
}
/* * Send a command to modify the PHY context based on the current HW * configuration. Note that the function does not check that the configuration * changed.
*/ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, conststruct cfg80211_chan_def *chandef, conststruct cfg80211_chan_def *ap,
u8 chains_static, u8 chains_dynamic)
{ enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
ctxt->channel->band != chandef->chan->band) { int ret;
/* ... remove it here ...*/
ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, NULL,
chains_static, chains_dynamic,
FW_CTXT_ACTION_REMOVE); if (ret) return ret;
/* ... and proceed to add it again */
action = FW_CTXT_ACTION_ADD;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.