/* * Monitor mode handling is a tad complicated because the firmware requires * an interface to be created exclusively, while mac80211 doesn't associate * an interface with the mode. * * So, for now, only one monitor interface can be configured. */ static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.index = priv->mon_vif_idx; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); if (ret) { ath_err(common, "Unable to remove monitor interface at idx: %d\n", priv->mon_vif_idx); } priv->nvifs--; priv->vif_slot &= ~(1 << priv->mon_vif_idx); }
static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_tx_queue_info qi; int ret = 0, qnum; if (queue >= WME_NUM_AC) return 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); qi.tqi_aifs = params->aifs; qi.tqi_cwmin = params->cw_min; qi.tqi_cwmax = params->cw_max; qi.tqi_burstTime = params->txop; qnum = get_hw_qnum(queue, priv->hwq_map); ath_dbg(common, ATH_DBG_CONFIG, "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", queue, qnum, params->aifs, params->cw_min, params->cw_max, params->txop); ret = ath_htc_txq_update(priv, qnum, &qi); if (ret) { ath_err(common, "TXQ Update failed\n"); goto out; } if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) && (qnum == priv->hwq_map[WME_AC_BE])) ath9k_htc_beaconq_config(priv); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
bool ath_hw_keyreset(struct ath_common *common, u16 entry) { u32 keyType; void *ah = common->ah; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); if (keyType == AR_KEYTABLE_TYPE_TKIP) { u16 micentry = entry + 64; REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) { REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); } } REGWRITE_BUFFER_FLUSH(ah); return true; }
static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); switch (action) { case IEEE80211_AMPDU_RX_START: break; case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: ista = (struct ath9k_htc_sta *) sta->drv_priv; spin_lock_bh(&priv->tx.tx_lock); ista->tid_state[tid] = AGGR_OPERATIONAL; spin_unlock_bh(&priv->tx.tx_lock); break; default: ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; struct ath9k_tx_queue_info qi, qi_be; memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info)); ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); if (priv->ah->opmode == NL80211_IFTYPE_AP) { qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; } else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { int qnum = priv->hwq_map[WME_AC_BE]; ath9k_hw_get_txq_props(ah, qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; /* * For WIFI Beacon Distribution * Long slot time : 2x cwmin * Short slot time : 4x cwmin */ if (ah->slottime == ATH9K_SLOT_TIME_20) qi.tqi_cwmin = 2*qi_be.tqi_cwmin; else qi.tqi_cwmin = 4*qi_be.tqi_cwmin; qi.tqi_cwmax = qi_be.tqi_cwmax; } if (!ath9k_hw_set_txq_props(ah, priv->beaconq, &qi)) { ath_err(ath9k_hw_common(ah), "Unable to update beacon queue %u!\n", priv->beaconq); } else { ath9k_hw_resettxqueue(ah, priv->beaconq); } }
static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; int ret; u8 cmd_rsp, sta_idx; if (sta) { ista = (struct ath9k_htc_sta *) sta->drv_priv; sta_idx = ista->index; } else { sta_idx = priv->vif_sta_pos[avp->index]; } WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); if (ret) { if (sta) ath_err(common, "Unable to remove station entry for: %pM\n", sta->addr); return ret; } if (sta) { ath_dbg(common, CONFIG, "Removed a station entry for: %pM (idx: %d)\n", sta->addr, sta_idx); } else { ath_dbg(common, CONFIG, "Removed a station entry for VIF %d (idx: %d)\n", avp->index, sta_idx); } priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; return 0; }
static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); int ret = 0; u8 cmd_rsp, sta_idx; __ath9k_htc_remove_monitor_interface(priv); sta_idx = 0; /* Only single interface, for now */ WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); if (ret) { ath_err(common, "Unable to remove station entry for monitor mode\n"); return ret; } priv->nstations--; priv->ah->is_monitoring = false; return 0; }
void ath9k_htc_radio_enable(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); int ret; u8 cmd_rsp; if (!ah->curchan) ah->curchan = ath9k_cmn_get_curchannel(hw, ah); /* Reset the HW */ ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); if (ret) { ath_err(common, "Unable to reset hardware; reset status %d (freq %u MHz)\n", ret, ah->curchan->channel); } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); /* Start RX */ WMI_CMD(WMI_START_RECV_CMDID); ath9k_host_rx_init(priv); /* Start TX */ htc_start(priv->htc); spin_lock_bh(&priv->tx.tx_lock); priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP; spin_unlock_bh(&priv->tx.tx_lock); ieee80211_wake_queues(hw); WMI_CMD(WMI_ENABLE_INTR_CMDID); /* Enable LED */ ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(ah, ah->led_pin, 0); }
int ath9k_cmn_spectral_scan_config(struct ath_common *common, struct ath_spec_scan_priv *spec_priv, enum spectral_mode spectral_mode) { struct ath_hw *ah = spec_priv->ah; if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { ath_err(common, "spectrum analyzer not implemented on this hardware\n"); return -1; } switch (spectral_mode) { case SPECTRAL_DISABLED: spec_priv->spec_config.enabled = 0; break; case SPECTRAL_BACKGROUND: /* send endless samples. * TODO: is this really useful for "background"? */ spec_priv->spec_config.endless = 1; spec_priv->spec_config.enabled = 1; break; case SPECTRAL_CHANSCAN: case SPECTRAL_MANUAL: spec_priv->spec_config.endless = 0; spec_priv->spec_config.enabled = 1; break; default: return -1; } ath_ps_ops(common)->wakeup(common); ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config); ath_ps_ops(common)->restore(common); spec_priv->spectral_mode = spectral_mode; return 0; }
static bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac) { u32 macHi, macLo; u32 unicast_flag = AR_KEYTABLE_VALID; void *ah = common->ah; if (entry >= common->keymax) { ath_err(common, "keycache entry %u out of range\n", entry); return false; } if (mac != NULL) { /* * AR_KEYTABLE_VALID indicates that the address is a unicast * address, which must match the transmitter address for * decrypting frames. * Not setting this bit allows the hardware to use the key * for multicast frame decryption. */ if (mac[0] & 0x01) unicast_flag = 0; #if 0 // by bbelief macLo = get_unaligned_le32(mac); macHi = get_unaligned_le16(mac + 4); #else macHi = (mac[5] << 8) | mac[4]; macLo = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]; #endif macLo >>= 1; macLo |= (macHi & 1) << 31; macHi >>= 1; } else {
bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, int subtype) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info qi; int qnum; memset(&qi, 0, sizeof(qi)); ATH9K_HTC_INIT_TXQ(subtype); qnum = ath9k_hw_setuptxqueue(priv->ah, ATH9K_TX_QUEUE_DATA, &qi); if (qnum == -1) return false; if (qnum >= ARRAY_SIZE(priv->hwq_map)) { ath_err(common, "qnum %u out of range, max %zu!\n", qnum, ARRAY_SIZE(priv->hwq_map)); ath9k_hw_releasetxqueue(ah, qnum); return false; } priv->hwq_map[subtype] = qnum; return true; }
static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc; struct ieee80211_channel *channel = hw->conf.chandef.chan; struct ath9k_hw_cal_data *caldata = NULL; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; int ret; if (test_bit(ATH_OP_INVALID, &common->op_flags)) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); ath9k_htc_ps_wakeup(priv); ath9k_htc_stop_ani(priv); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); ath9k_wmi_event_drain(priv); ath_dbg(common, CONFIG, "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), fastcc); if (!fastcc) caldata = &priv->caldata; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_err(common, "Unable to reset channel (%u Mhz) reset status %d\n", channel->center_freq, ret); goto err; } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); WMI_CMD(WMI_START_RECV_CMDID); if (ret) goto err; ath9k_host_rx_init(priv); mode = ath9k_htc_get_curmode(priv, hchan); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); if (ret) goto err; WMI_CMD(WMI_ENABLE_INTR_CMDID); if (ret) goto err; htc_start(priv->htc); if (!test_bit(ATH_OP_SCANNING, &common->op_flags) && !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) ath9k_htc_vif_reconfig(priv); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); err: ath9k_htc_ps_restore(priv); return ret; }
static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; struct ath9k_htc_target_sta tsta; int ret = 0; u8 cmd_rsp; if (priv->nvifs > 0) return -ENOBUFS; if (priv->nstations >= ATH9K_HTC_MAX_STA) return -ENOBUFS; /* * Add an interface. */ memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.opmode = cpu_to_be32(HTC_M_MONITOR); priv->ah->opmode = NL80211_IFTYPE_MONITOR; hvif.index = priv->nvifs; WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) return ret; priv->nvifs++; /* * Associate a station with the interface for packet injection. */ memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN); tsta.is_vif_sta = 1; tsta.sta_index = priv->nstations; tsta.vif_index = hvif.index; tsta.maxampdu = 0xffff; WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { ath_err(common, "Unable to add station entry for monitor mode\n"); goto err_vif; } priv->nstations++; /* * Set chainmask etc. on the target. */ ret = ath9k_htc_update_cap_target(priv); if (ret) ath_dbg(common, ATH_DBG_CONFIG, "Failed to update capability in target\n"); priv->ah->is_monitoring = true; return 0; err_vif: /* * Remove the interface from the target. */ __ath9k_htc_remove_monitor_interface(priv); return ret; }
static int ath9k_htc_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); switch (vif->type) { case NL80211_IFTYPE_STATION: hvif.opmode = HTC_M_STA; break; case NL80211_IFTYPE_ADHOC: hvif.opmode = HTC_M_IBSS; break; case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; case NL80211_IFTYPE_MESH_POINT: hvif.opmode = HTC_M_WDS; /* close enough */ break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } /* Index starts from zero on the target */ avp->index = hvif.index = ffz(priv->vif_slot); hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); if (ret) { WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; } ath9k_htc_set_mac_bssid_mask(priv, vif); priv->vif_slot |= (1 << avp->index); priv->nvifs++; INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_MESH_POINT) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && !test_bit(ATH_OP_ANI_RUN, &common->op_flags)) { ath9k_hw_set_tsfadjust(priv->ah, true); ath9k_htc_start_ani(priv); } ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_conf *conf = &hw->conf; bool chip_reset = false; int ret = 0; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); if (changed & IEEE80211_CONF_CHANGE_IDLE) { mutex_lock(&priv->htc_pm_lock); priv->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); if (!priv->ps_idle) chip_reset = true; mutex_unlock(&priv->htc_pm_lock); } /* * Monitor interface should be added before * IEEE80211_CONF_CHANGE_CHANNEL is handled. */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if ((conf->flags & IEEE80211_CONF_MONITOR) && !priv->ah->is_monitoring) ath9k_htc_add_monitor_interface(priv); else if (priv->ah->is_monitoring) ath9k_htc_remove_monitor_interface(priv); } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) { struct ieee80211_channel *curchan = hw->conf.chandef.chan; int pos = curchan->hw_value; ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); ret = -EINVAL; goto out; } } if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); priv->ps_enabled = true; } else { priv->ps_enabled = false; cancel_work_sync(&priv->ps_work); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); } } if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->txpowlimit = 2 * conf->power_level; ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); } out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, const struct ath9k_tx_queue_info *qinfo) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_tx_queue_info *qi; int q; switch (type) { case ATH9K_TX_QUEUE_BEACON: q = ATH9K_NUM_TX_QUEUES - 1; break; case ATH9K_TX_QUEUE_CAB: q = ATH9K_NUM_TX_QUEUES - 2; break; case ATH9K_TX_QUEUE_PSPOLL: q = 1; break; case ATH9K_TX_QUEUE_UAPSD: q = ATH9K_NUM_TX_QUEUES - 3; break; case ATH9K_TX_QUEUE_DATA: for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++) if (ah->txq[q].tqi_type == ATH9K_TX_QUEUE_INACTIVE) break; if (q == ATH9K_NUM_TX_QUEUES) { ath_err(common, "No available TX queue\n"); return -1; } break; default: ath_err(common, "Invalid TX queue type: %u\n", type); return -1; } ath_dbg(common, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q); qi = &ah->txq[q]; if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { ath_err(common, "TX queue: %u already active\n", q); return -1; } memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); qi->tqi_type = type; if (qinfo == NULL) { qi->tqi_qflags = TXQ_FLAG_TXOKINT_ENABLE | TXQ_FLAG_TXERRINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE; qi->tqi_aifs = INIT_AIFS; qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT; qi->tqi_cwmax = INIT_CWMAX; qi->tqi_shretry = INIT_SH_RETRY; qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_physCompBuf = 0; } else { qi->tqi_physCompBuf = qinfo->tqi_physCompBuf; (void) ath9k_hw_set_txq_props(ah, q, qinfo); } return q; }
static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath9k_htc_priv *priv = file->private_data; struct ath_common *common = ath9k_hw_common(priv->ah); struct base_eep_header *pBase = NULL; unsigned int len = 0, size = 1500; ssize_t retval = 0; char *buf; /* * This can be done since all the 3 EEPROM families have the * same base header upto a certain point, and we are interested in * the data only upto that point. */ if (AR_SREV_9271(priv->ah)) pBase = (struct base_eep_header *) &priv->ah->eeprom.map4k.baseEepHeader; else if (priv->ah->hw_version.usbdev == AR9280_USB) pBase = (struct base_eep_header *) &priv->ah->eeprom.def.baseEepHeader; else if (priv->ah->hw_version.usbdev == AR9287_USB) pBase = (struct base_eep_header *) &priv->ah->eeprom.map9287.baseEepHeader; if (pBase == NULL) { ath_err(common, "Unknown EEPROM type\n"); return 0; } buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; len += snprintf(buf + len, size - len, "%20s : %10d\n", "Major Version", pBase->version >> 12); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Minor Version", pBase->version & 0xFFF); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Checksum", pBase->checksum); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Length", pBase->length); len += snprintf(buf + len, size - len, "%20s : %10d\n", "RegDomain1", pBase->regDmn[0]); len += snprintf(buf + len, size - len, "%20s : %10d\n", "RegDomain2", pBase->regDmn[1]); len += snprintf(buf + len, size - len, "%20s : %10d\n", "TX Mask", pBase->txMask); len += snprintf(buf + len, size - len, "%20s : %10d\n", "RX Mask", pBase->rxMask); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Allow 5GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Allow 2GHz", !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 2GHz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 2GHz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 5Ghz HT20", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Disable 5Ghz HT40", !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Big Endian", !!(pBase->eepMisc & 0x01)); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Cal Bin Major Ver", (pBase->binBuildNumber >> 24) & 0xFF); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Cal Bin Minor Ver", (pBase->binBuildNumber >> 16) & 0xFF); len += snprintf(buf + len, size - len, "%20s : %10d\n", "Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); /* * UB91 specific data. */ if (AR_SREV_9271(priv->ah)) { struct base_eep_header_4k *pBase4k = &priv->ah->eeprom.map4k.baseEepHeader; len += snprintf(buf + len, size - len, "%20s : %10d\n", "TX Gain type", pBase4k->txGainType); } /* * UB95 specific data. */ if (priv->ah->hw_version.usbdev == AR9287_USB) { struct base_eep_ar9287_header *pBase9287 = &priv->ah->eeprom.map9287.baseEepHeader; len += snprintf(buf + len, size - len, "%20s : %10ddB\n", "Power Table Offset", pBase9287->pwrTableOffset); len += snprintf(buf + len, size - len, "%20s : %10d\n", "OpenLoop Power Ctrl", pBase9287->openLoopPwrCntl); } len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", pBase->macAddr); if (len > size) len = size; retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return retval; }
static int ath9k_htc_start(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *curchan = hw->conf.channel; struct ath9k_channel *init_channel; int ret = 0; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; mutex_lock(&priv->mutex); ath_dbg(common, ATH_DBG_CONFIG, "Starting driver with initial channel: %d MHz\n", curchan->center_freq); /* Ensure that HW is awake before flushing RX */ ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); WMI_CMD(WMI_FLUSH_RECV_CMDID); /* setup initial channel */ init_channel = ath9k_cmn_get_curchannel(hw, ah); ath9k_hw_htc_resetinit(ah); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { ath_err(common, "Unable to reset hardware; reset status %d (freq %u MHz)\n", ret, curchan->center_freq); mutex_unlock(&priv->mutex); return ret; } ath_update_txpow(priv); mode = ath9k_htc_get_curmode(priv, init_channel); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); WMI_CMD(WMI_ATH_INIT_CMDID); WMI_CMD(WMI_START_RECV_CMDID); ath9k_host_rx_init(priv); priv->op_flags &= ~OP_INVALID; htc_start(priv->htc); spin_lock_bh(&priv->tx_lock); priv->tx_queues_stop = false; spin_unlock_bh(&priv->tx_lock); ieee80211_wake_queues(hw); if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) { ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, AR_STOMP_LOW_WLAN_WGHT); ath9k_hw_btcoex_enable(ah); ath_htc_resume_btcoex_work(priv); } mutex_unlock(&priv->mutex); return ret; }
static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc; struct ieee80211_channel *channel = hw->conf.channel; struct ath9k_hw_cal_data *caldata; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; int ret; if (priv->op_flags & OP_INVALID) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); ath9k_htc_ps_wakeup(priv); htc_stop(priv->htc); WMI_CMD(WMI_DISABLE_INTR_CMDID); WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); WMI_CMD(WMI_STOP_RECV_CMDID); ath_dbg(common, ATH_DBG_CONFIG, "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n", priv->ah->curchan->channel, channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf), fastcc); caldata = &priv->caldata[channel->hw_value]; ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (ret) { ath_err(common, "Unable to reset channel (%u Mhz) reset status %d\n", channel->center_freq, ret); goto err; } ath_update_txpow(priv); WMI_CMD(WMI_START_RECV_CMDID); if (ret) goto err; ath9k_host_rx_init(priv); mode = ath9k_htc_get_curmode(priv, hchan); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); if (ret) goto err; WMI_CMD(WMI_ENABLE_INTR_CMDID); if (ret) goto err; htc_start(priv->htc); err: ath9k_htc_ps_restore(priv); return ret; }
static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_buf *bf; struct ath_vif *avp; struct sk_buff *skb; struct ath_txq *cabq; struct ieee80211_tx_info *info; int cabq_depth; ath9k_reset_beacon_status(sc); avp = (void *)vif->drv_priv; cabq = sc->beacon.cabq; if ((avp->av_bcbuf == NULL) || !avp->is_bslot_active) return NULL; /* Release the old beacon first */ bf = avp->av_bcbuf; skb = bf->bf_mpdu; if (skb) { dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_buf_addr = 0; } /* Get a new beacon from mac80211 */ skb = ieee80211_beacon_get(hw, vif); bf->bf_mpdu = skb; if (skb == NULL) return NULL; ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = avp->tsf_adjust; info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { /* * TODO: make sure the seq# gets assigned properly (vs. other * TX frames) */ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; sc->tx.seq_no += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; bf->bf_buf_addr = 0; ath_err(common, "dma_mapping_error on beaconing\n"); return NULL; } skb = ieee80211_get_buffered_bc(hw, vif); /* * if the CABQ traffic from previous DTIM is pending and the current * beacon is also a DTIM. * 1) if there is only one vif let the cab traffic continue. * 2) if there are more than one vif and we are using staggered * beacons, then drain the cabq by dropping all the frames in * the cabq so that the current vifs cab traffic can be scheduled. */ spin_lock_bh(&cabq->axq_lock); cabq_depth = cabq->axq_depth; spin_unlock_bh(&cabq->axq_lock); if (skb && cabq_depth) { if (sc->nvifs > 1) { ath_dbg(common, BEACON, "Flushing previous cabq traffic\n"); ath_draintxq(sc, cabq, false); } } ath_beacon_setup(sc, vif, bf, info->control.rates[0].idx); while (skb) { ath_tx_cabq(hw, skb); skb = ieee80211_get_buffered_bc(hw, vif); } return bf; }
/* * Set/change channels. If the channel is really being changed, it's done * by reseting the chip. To accomplish this we must first cleanup any pending * DMA, then restart stuff. */ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *hchan) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; bool fastcc = true, stopped; struct ieee80211_channel *channel = hw->conf.channel; struct ath9k_hw_cal_data *caldata = NULL; int r; if (sc->sc_flags & SC_OP_INVALID) return -EIO; sc->hw_busy_count = 0; del_timer_sync(&common->ani.timer); cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->hw_check_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); /* * This is only performed if the channel settings have * actually changed. * * To switch channels clear any pending DMA operations; * wait long enough for the RX fifo to drain, reset the * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ ath9k_hw_disable_interrupts(ah); stopped = ath_drain_all_txq(sc, false); if (!ath_stoprecv(sc)) stopped = false; if (!ath9k_hw_check_alive(ah)) stopped = false; /* XXX: do not flush receive queue here. We don't want * to flush data frames already in queue because of * changing channel. */ if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL)) fastcc = false; if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) caldata = &sc->caldata; ath_dbg(common, ATH_DBG_CONFIG, "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n", sc->sc_ah->curchan->channel, channel->center_freq, conf_is_ht40(conf), fastcc); r = ath9k_hw_reset(ah, hchan, caldata, fastcc); if (r) { ath_err(common, "Unable to reset channel (%u MHz), reset status %d\n", channel->center_freq, r); goto ps_restore; } if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); r = -EIO; goto ps_restore; } ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); ath9k_hw_set_interrupts(ah, ah->imask); if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) { if (sc->sc_flags & SC_OP_BEACONS) ath_set_beacon(sc); ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); if (!common->disable_ani) ath_start_ani(common); } ps_restore: ieee80211_wake_queues(hw); spin_unlock_bh(&sc->sc_pcu_lock); ath9k_ps_restore(sc); return r; }
static int ath9k_htc_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); if (priv->nvifs >= ATH9K_HTC_MAX_VIF) { mutex_unlock(&priv->mutex); return -ENOBUFS; } if (priv->num_ibss_vif || (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { ath_err(common, "IBSS coexistence with other modes is not allowed\n"); mutex_unlock(&priv->mutex); return -ENOBUFS; } if (((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) && ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) { ath_err(common, "Max. number of beaconing interfaces reached\n"); mutex_unlock(&priv->mutex); return -ENOBUFS; } ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); switch (vif->type) { case NL80211_IFTYPE_STATION: hvif.opmode = HTC_M_STA; break; case NL80211_IFTYPE_ADHOC: hvif.opmode = HTC_M_IBSS; break; case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); ret = -EOPNOTSUPP; goto out; } /* Index starts from zero on the target */ avp->index = hvif.index = ffz(priv->vif_slot); hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); if (ret) { WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; } ath9k_htc_set_mac_bssid_mask(priv, vif); priv->vif_slot |= (1 << avp->index); priv->nvifs++; INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && !(priv->op_flags & OP_ANI_RUNNING)) { ath9k_hw_set_tsfadjust(priv->ah, 1); ath9k_htc_start_ani(priv); } ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); return ret; }
int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = sc->hw; struct ath_common *common; struct ath_hw *ah; int error = 0; struct ath_regulatory *reg; /* Bring up device */ error = ath9k_init_softc(devid, sc, bus_ops); if (error) return error; ah = sc->sc_ah; common = ath9k_hw_common(ah); ath9k_set_hw_capab(sc, hw); /* Initialize regulatory */ error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) goto deinit; reg = &common->regulatory; /* Setup TX DMA */ error = ath_tx_init(sc, ATH_TXBUF); if (error != 0) goto deinit; /* Setup RX DMA */ error = ath_rx_init(sc, ATH_RXBUF); if (error != 0) goto deinit; ath9k_init_txpower_limits(sc); #ifdef CONFIG_MAC80211_LEDS /* must be initialized before ieee80211_register_hw */ sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw, IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink, ARRAY_SIZE(ath9k_tpt_blink)); #endif /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) goto rx_cleanup; error = ath9k_init_debug(ah); if (error) { ath_err(common, "Unable to create debugfs files\n"); goto unregister; } /* Handle world regulatory */ if (!ath_is_world_regd(reg)) { error = regulatory_hint(hw->wiphy, reg->alpha2); if (error) goto debug_cleanup; } ath_init_leds(sc); ath_start_rfkill_poll(sc); return 0; debug_cleanup: ath9k_deinit_debug(sc); unregister: ieee80211_unregister_hw(hw); rx_cleanup: ath_rx_cleanup(sc); deinit: ath9k_deinit_softc(sc); return error; }
static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_conf *conf = &hw->conf; mutex_lock(&priv->mutex); if (changed & IEEE80211_CONF_CHANGE_IDLE) { bool enable_radio = false; bool idle = !!(conf->flags & IEEE80211_CONF_IDLE); mutex_lock(&priv->htc_pm_lock); if (!idle && priv->ps_idle) enable_radio = true; priv->ps_idle = idle; mutex_unlock(&priv->htc_pm_lock); if (enable_radio) { ath_dbg(common, CONFIG, "not-idle: enabling radio\n"); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); ath9k_htc_radio_enable(hw); } } /* * Monitor interface should be added before * IEEE80211_CONF_CHANGE_CHANNEL is handled. */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if ((conf->flags & IEEE80211_CONF_MONITOR) && !priv->ah->is_monitoring) ath9k_htc_add_monitor_interface(priv); else if (priv->ah->is_monitoring) ath9k_htc_remove_monitor_interface(priv); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); ath9k_cmn_update_ichannel(&priv->ah->channels[pos], hw->conf.channel, hw->conf.channel_type); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); mutex_unlock(&priv->mutex); return -EINVAL; } } if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); priv->ps_enabled = true; } else { priv->ps_enabled = false; cancel_work_sync(&priv->ps_work); ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); } } if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->txpowlimit = 2 * conf->power_level; ath9k_cmn_update_txpow(priv->ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); } if (changed & IEEE80211_CONF_CHANGE_IDLE) { mutex_lock(&priv->htc_pm_lock); if (!priv->ps_idle) { mutex_unlock(&priv->htc_pm_lock); goto out; } mutex_unlock(&priv->htc_pm_lock); ath_dbg(common, CONFIG, "idle: disabling radio\n"); ath9k_htc_radio_disable(hw); } out: mutex_unlock(&priv->mutex); return 0; }
static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; struct ath9k_htc_target_sta tsta; int ret = 0, sta_idx; u8 cmd_rsp; if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) || (priv->nstations >= ATH9K_HTC_MAX_STA)) { ret = -ENOBUFS; goto err_vif; } sta_idx = ffz(priv->sta_slot); if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) { ret = -ENOBUFS; goto err_vif; } /* * Add an interface. */ memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.opmode = HTC_M_MONITOR; hvif.index = ffz(priv->vif_slot); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto err_vif; /* * Assign the monitor interface index as a special case here. * This is needed when the interface is brought down. */ priv->mon_vif_idx = hvif.index; priv->vif_slot |= (1 << hvif.index); /* * Set the hardware mode to monitor only if there are no * other interfaces. */ if (!priv->nvifs) priv->ah->opmode = NL80211_IFTYPE_MONITOR; priv->nvifs++; /* * Associate a station with the interface for packet injection. */ memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN); tsta.is_vif_sta = 1; tsta.sta_index = sta_idx; tsta.vif_index = hvif.index; tsta.maxampdu = cpu_to_be16(0xffff); WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { ath_err(common, "Unable to add station entry for monitor mode\n"); goto err_sta; } priv->sta_slot |= (1 << sta_idx); priv->nstations++; priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx; priv->ah->is_monitoring = true; ath_dbg(common, CONFIG, "Attached a monitor interface at idx: %d, sta idx: %d\n", priv->mon_vif_idx, sta_idx); return 0; err_sta: /* * Remove the interface from the target. */ __ath9k_htc_remove_monitor_interface(priv); err_vif: ath_dbg(common, FATAL, "Unable to attach a monitor interface\n"); return ret; }
/* * This function will allocate both the DMA descriptor structure, and the * buffers it contains. These are used to contain the descriptors used * by the system. */ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, struct list_head *head, const char *name, int nbuf, int ndesc, bool is_tx) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ds; struct ath_buf *bf; int i, bsize, error, desc_len; ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", name, nbuf, ndesc); INIT_LIST_HEAD(head); if (is_tx) desc_len = sc->sc_ah->caps.tx_desc_len; else desc_len = sizeof(struct ath_desc); /* ath_desc must be a multiple of DWORDs */ if ((desc_len % 4) != 0) { ath_err(common, "ath_desc not DWORD aligned\n"); BUG_ON((desc_len % 4) != 0); error = -ENOMEM; goto fail; } dd->dd_desc_len = desc_len * nbuf * ndesc; /* * Need additional DMA memory because we can't use * descriptors that cross the 4K page boundary. Assume * one skipped descriptor per 4K page. */ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { u32 ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); u32 dma_len; while (ndesc_skipped) { dma_len = ndesc_skipped * desc_len; dd->dd_desc_len += dma_len; ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); } } /* allocate descriptors */ dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, &dd->dd_desc_paddr, GFP_KERNEL); if (dd->dd_desc == NULL) { error = -ENOMEM; goto fail; } ds = (u8 *) dd->dd_desc; ath_dbg(common, CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", name, ds, (u32) dd->dd_desc_len, ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); /* allocate buffers */ bsize = sizeof(struct ath_buf) * nbuf; bf = kzalloc(bsize, GFP_KERNEL); if (bf == NULL) { error = -ENOMEM; goto fail2; } dd->dd_bufptr = bf; for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { bf->bf_desc = ds; bf->bf_daddr = DS2PHYS(dd, ds); if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { /* * Skip descriptor addresses which can cause 4KB * boundary crossing (addr + length) with a 32 dword * descriptor fetch. */ while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { BUG_ON((caddr_t) bf->bf_desc >= ((caddr_t) dd->dd_desc + dd->dd_desc_len)); ds += (desc_len * ndesc); bf->bf_desc = ds; bf->bf_daddr = DS2PHYS(dd, ds); } } list_add_tail(&bf->list, head); } return 0; fail2: dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); fail: memset(dd, 0, sizeof(*dd)); return error; }
static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_sta tsta; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; int ret, sta_idx; u8 cmd_rsp; u16 maxampdu; if (priv->nstations >= ATH9K_HTC_MAX_STA) return -ENOBUFS; sta_idx = ffz(priv->sta_slot); if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) return -ENOBUFS; memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); if (sta) { ista = (struct ath9k_htc_sta *) sta->drv_priv; memcpy(&tsta.macaddr, sta->addr, ETH_ALEN); memcpy(&tsta.bssid, common->curbssid, ETH_ALEN); ista->index = sta_idx; tsta.is_vif_sta = 0; maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); tsta.maxampdu = cpu_to_be16(maxampdu); } else { memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); tsta.is_vif_sta = 1; tsta.maxampdu = cpu_to_be16(0xffff); } tsta.sta_index = sta_idx; tsta.vif_index = avp->index; WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { if (sta) ath_err(common, "Unable to add station entry for: %pM\n", sta->addr); return ret; } if (sta) { ath_dbg(common, CONFIG, "Added a station entry for: %pM (idx: %d)\n", sta->addr, tsta.sta_index); } else { ath_dbg(common, CONFIG, "Added a station entry for VIF %d (idx: %d)\n", avp->index, tsta.sta_index); } priv->sta_slot |= (1 << sta_idx); priv->nstations++; if (!sta) priv->vif_sta_pos[avp->index] = sta_idx; return 0; }
int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { struct ieee80211_hw *hw = sc->hw; struct ath_common *common; struct ath_hw *ah; int error = 0; struct ath_regulatory *reg; /* Bring up device */ error = ath9k_init_softc(devid, sc, bus_ops); if (error != 0) goto error_init; ah = sc->sc_ah; common = ath9k_hw_common(ah); ath9k_set_hw_capab(sc, hw); /* Initialize regulatory */ error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) goto error_regd; reg = &common->regulatory; /* Setup TX DMA */ error = ath_tx_init(sc, ATH_TXBUF); if (error != 0) goto error_tx; /* Setup RX DMA */ error = ath_rx_init(sc, ATH_RXBUF); if (error != 0) goto error_rx; ath9k_init_txpower_limits(sc); #ifdef CONFIG_MAC80211_LEDS /* must be initialized before ieee80211_register_hw */ sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw, IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink, ARRAY_SIZE(ath9k_tpt_blink)); #endif INIT_WORK(&sc->hw_reset_work, ath_reset_work); INIT_WORK(&sc->hw_check_work, ath_hw_check); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) goto error_register; error = ath9k_init_debug(ah); if (error) { ath_err(common, "Unable to create debugfs files\n"); goto error_world; } /* Handle world regulatory */ if (!ath_is_world_regd(reg)) { error = regulatory_hint(hw->wiphy, reg->alpha2); if (error) goto error_world; } setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; ath_init_leds(sc); ath_start_rfkill_poll(sc); return 0; error_world: ieee80211_unregister_hw(hw); error_register: ath_rx_cleanup(sc); error_rx: ath_tx_cleanup(sc); error_tx: /* Nothing */ error_regd: ath9k_deinit_softc(sc); error_init: return error; }
static int ath9k_htc_start(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_channel *curchan = hw->conf.chandef.chan; struct ath9k_channel *init_channel; int ret = 0; enum htc_phymode mode; __be16 htc_mode; u8 cmd_rsp; mutex_lock(&priv->mutex); ath_dbg(common, CONFIG, "Starting driver with initial channel: %d MHz\n", curchan->center_freq); /* Ensure that HW is awake before flushing RX */ ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); WMI_CMD(WMI_FLUSH_RECV_CMDID); /* setup initial channel */ init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { ath_err(common, "Unable to reset hardware; reset status %d (freq %u MHz)\n", ret, curchan->center_freq); mutex_unlock(&priv->mutex); return ret; } ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, &priv->curtxpow); mode = ath9k_htc_get_curmode(priv, init_channel); htc_mode = cpu_to_be16(mode); WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); WMI_CMD(WMI_ATH_INIT_CMDID); WMI_CMD(WMI_START_RECV_CMDID); ath9k_host_rx_init(priv); ret = ath9k_htc_update_cap_target(priv, 0); if (ret) ath_dbg(common, CONFIG, "Failed to update capability in target\n"); clear_bit(ATH_OP_INVALID, &common->op_flags); htc_start(priv->htc); spin_lock_bh(&priv->tx.tx_lock); priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP; spin_unlock_bh(&priv->tx.tx_lock); ieee80211_wake_queues(hw); mod_timer(&priv->tx.cleanup_timer, jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL)); ath9k_htc_start_btcoex(priv); mutex_unlock(&priv->mutex); return ret; }
int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, bool is_full_sleep) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 regval, i; ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n", is_full_sleep, is_2g); if (!mci->gpm_addr && !mci->sched_addr) { ath_err(common, "MCI GPM and schedule buffers are not allocated\n"); return -ENOMEM; } if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) { ath_err(common, "BTCOEX control register is dead\n"); return -EINVAL; } /* Program MCI DMA related registers */ REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr); REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len); REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr); /* * To avoid MCI state machine be affected by incoming remote MCI msgs, * MCI mode will be enabled later, right before reset the MCI TX and RX. */ regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) | SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) | SM(1, AR_BTCOEX_CTRL_PA_SHARED) | SM(1, AR_BTCOEX_CTRL_LNA_SHARED) | SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) | SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) | SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); if (AR_SREV_9565(ah)) { regval |= SM(1, AR_BTCOEX_CTRL_NUM_ANTENNAS) | SM(1, AR_BTCOEX_CTRL_RX_CHAIN_MASK); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_TX_CHAIN_MASK, 0x1); } else { regval |= SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) | SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK); } REG_WRITE(ah, AR_BTCOEX_CTRL, regval); if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) ar9003_mci_osla_setup(ah, true); else ar9003_mci_osla_setup(ah, false); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3, AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 0); REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); /* Set the time out to 3.125ms (5 BT slots) */ REG_RMW_FIELD(ah, AR_BTCOEX_WL_LNA, AR_BTCOEX_WL_LNA_TIMEOUT, 0x3D090); /* concurrent tx priority */ if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) { REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE, 0); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_TXPWR_THRESH, 0x7f); REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_REDUCE_TXPWR, 0); for (i = 0; i < 8; i++) REG_WRITE(ah, AR_BTCOEX_MAX_TXPWR(i), 0x7f7f7f7f); } regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV); REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval); REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN); /* Resetting the Rx and Tx paths of MCI */ regval = REG_READ(ah, AR_MCI_COMMAND2); regval |= SM(1, AR_MCI_COMMAND2_RESET_TX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); udelay(1); regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); if (is_full_sleep) { ar9003_mci_mute_bt(ah); udelay(100); } /* Check pending GPM msg before MCI Reset Rx */ ar9003_mci_check_gpm_offset(ah); regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); udelay(1); regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); ar9003_mci_get_next_gpm_offset(ah, true, NULL); REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) | SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM))); REG_CLR_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); ar9003_mci_observation_set_up(ah); mci->ready = true; ar9003_mci_prep_interface(ah); if (AR_SREV_9565(ah)) REG_RMW_FIELD(ah, AR_MCI_DBG_CNT_CTRL, AR_MCI_DBG_CNT_CTRL_ENABLE, 0); if (en_int) ar9003_mci_enable_interrupt(ah); return 0; }