bool
_il_grab_nic_access(struct il_priv *il)
{ int ret;
u32 val;
/* this bit wakes up the NIC */
_il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
/* * These bits say the device is running, and should keep running for * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), * but they do not indicate that embedded SRAM is restored yet; * 3945 and 4965 have volatile SRAM, and must save/restore contents * to/from host DRAM when sleeping/waking for power-saving. * Each direction takes approximately 1/4 millisecond; with this * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a * series of register accesses are expected (e.g. reading Event Log), * to keep device from sleeping. * * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that * SRAM is okay/restored. We don't check that here because this call * is just for hardware register access; but GP1 MAC_SLEEP check is a * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). *
*/
ret =
_il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
(CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); if (unlikely(ret < 0)) {
val = _il_rd(il, CSR_GP_CNTRL);
WARN_ONCE(1, "Timeout waiting for ucode processor access " "(CSR_GP_CNTRL 0x%08x)\n", val);
_il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); returnfalse;
}
ret = wait_event_timeout(il->wait_command_queue,
!test_bit(S_HCMD_ACTIVE, &il->status),
HOST_COMPLETE_TIMEOUT); if (!ret) { if (test_bit(S_HCMD_ACTIVE, &il->status)) {
IL_ERR("Error sending %s: time out after %dms.\n",
il_get_cmd_string(cmd->id),
jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
clear_bit(S_HCMD_ACTIVE, &il->status);
D_INFO("Clearing HCMD_ACTIVE for command %s\n",
il_get_cmd_string(cmd->id));
ret = -ETIMEDOUT; goto cancel;
}
}
if (test_bit(S_RFKILL, &il->status)) {
IL_ERR("Command %s aborted: RF KILL Switch\n",
il_get_cmd_string(cmd->id));
ret = -ECANCELED; goto fail;
} if (test_bit(S_FW_ERROR, &il->status)) {
IL_ERR("Command %s failed: FW Error\n",
il_get_cmd_string(cmd->id));
ret = -EIO; goto fail;
} if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
IL_ERR("Error: Response NULL in '%s'\n",
il_get_cmd_string(cmd->id));
ret = -EIO; goto cancel;
}
ret = 0; goto out;
cancel: if (cmd->flags & CMD_WANT_SKB) { /* * Cancel the CMD_WANT_SKB flag for the cmd in the * TX cmd queue. Otherwise in case the cmd comes * in later, it will possibly set an invalid * address (cmd->meta.source).
*/
il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB;
}
fail: if (cmd->reply_page) {
il_free_pages(il, cmd->reply_page);
cmd->reply_page = 0;
}
out: return ret;
}
EXPORT_SYMBOL(il_send_cmd_sync);
int
il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd)
{ if (cmd->flags & CMD_ASYNC) return il_send_cmd_async(il, cmd);
/* * Adjust led blink rate to compensate on a MAC Clock difference on every HW * Led blink rate analysis showed an average deviation of 0% on 3945, * 5% on 4965 HW. * Need to compensate on the led on/off time per HW according to the deviation * to achieve the desired led frequency * The calculation is: (100-averageDeviation)/100 * blinkTime * For code efficiency the calculation will be: * compensation = (100 - averageDeviation) * 64 / 100 * NewBlinkTime = (compensation * BlinkTime) / 64
*/ staticinline u8
il_blink_compensation(struct il_priv *il, u8 time, u16 compensation)
{ if (!compensation) {
IL_ERR("undefined blink compensation: " "use pre-defined blinking time\n"); return time;
}
return (u8) ((time * compensation) >> 6);
}
/* Set led pattern command */ staticint
il_led_cmd(struct il_priv *il, unsignedlong on, unsignedlong off)
{ struct il_led_cmd led_cmd = {
.id = IL_LED_LINK,
.interval = IL_DEF_LED_INTRVL
}; int ret;
if (!test_bit(S_READY, &il->status)) return -EBUSY;
if (il->blink_on == on && il->blink_off == off) return 0;
if (off == 0) { /* led is SOLID_ON */
on = IL_LED_SOLID;
}
D_LED("Led blink time compensation=%u\n",
il->cfg->led_compensation);
led_cmd.on =
il_blink_compensation(il, on,
il->cfg->led_compensation);
led_cmd.off =
il_blink_compensation(il, off,
il->cfg->led_compensation);
ret = il->ops->send_led_cmd(il, &led_cmd); if (!ret) {
il->blink_on = on;
il->blink_off = off;
} return ret;
}
/************************** EEPROM BANDS **************************** * * The il_eeprom_band definitions below provide the mapping from the * EEPROM contents to the specific channel number supported for each * band. * * For example, il_priv->eeprom.band_3_channels[4] from the band_3 * definition below maps to physical channel 42 in the 5.2GHz spectrum. * The specific geography and calibration information for that channel * is contained in the eeprom map itself. * * During init, we copy the eeprom information and channel map * information into il->channel_info_24/52 and il->channel_map_24/52 * * channel_map_24/52 provides the idx in the channel_info array for a * given channel. We have to have two separate maps as there is channel * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and * band_2 * * A value of 0xff stored in the channel_map indicates that the channel * is not supported by the hardware at all. * * A value of 0xfe in the channel_map indicates that the channel is not * valid for Tx with the current hardware. This means that * while the system can tune and receive on a given channel, it may not * be able to associate or transmit any frames on that * channel. There is no corresponding channel information for that * entry. *
*********************************************************************/
/* * il_eeprom_init - read EEPROM contents * * Load the EEPROM contents from adapter into il->eeprom * * NOTE: This routine uses the non-debug IO access functions.
*/ int
il_eeprom_init(struct il_priv *il)
{
__le16 *e;
u32 gp = _il_rd(il, CSR_EEPROM_GP); int sz; int ret; int addr;
ret = il_eeprom_verify_signature(il); if (ret < 0) {
IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp);
ret = -ENOENT; goto err;
}
/* Make sure driver (instead of uCode) is allowed to read EEPROM */
ret = il->ops->eeprom_acquire_semaphore(il); if (ret < 0) {
IL_ERR("Failed to acquire EEPROM semaphore.\n");
ret = -ENOENT; goto err;
}
/* eeprom is an array of 16bit values */ for (addr = 0; addr < sz; addr += sizeof(u16)) {
u32 r;
ret = 0;
done:
il->ops->eeprom_release_semaphore(il);
err: if (ret)
il_eeprom_free(il); /* Reset chip to save power until we load uCode during "up". */
il_apm_stop(il); return ret;
}
EXPORT_SYMBOL(il_eeprom_init);
/* * il_init_channel_map - Set up driver's info for all possible channels
*/ int
il_init_channel_map(struct il_priv *il)
{ int eeprom_ch_count = 0; const u8 *eeprom_ch_idx = NULL; conststruct il_eeprom_channel *eeprom_ch_info = NULL; int band, ch; struct il_channel_info *ch_info;
if (il->channel_count) {
D_EEPROM("Channel map already initialized.\n"); return 0;
}
D_EEPROM("Initializing regulatory info from EEPROM\n");
D_EEPROM("Parsing data for %d channels.\n", il->channel_count);
il->channel_info =
kcalloc(il->channel_count, sizeof(struct il_channel_info),
GFP_KERNEL); if (!il->channel_info) {
IL_ERR("Could not allocate channel_info\n");
il->channel_count = 0; return -ENOMEM;
}
ch_info = il->channel_info;
/* Loop through the 5 EEPROM bands adding them in order to the * channel map we maintain (that contains additional information than
* what just in the EEPROM) */ for (band = 1; band <= 5; band++) {
/* Loop through each band adding each of the channels */ for (ch = 0; ch < eeprom_ch_count; ch++) {
ch_info->channel = eeprom_ch_idx[ch];
ch_info->band =
(band ==
1) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
/* permanently store EEPROM's channel regulatory flags
* and max power in channel info database. */
ch_info->eeprom = eeprom_ch_info[ch];
/* Copy the run-time flags so they are there even on
* invalid channels */
ch_info->flags = eeprom_ch_info[ch].flags; /* First write that ht40 is not enabled, and then enable
* one by one */
ch_info->ht40_extension_channel =
IEEE80211_CHAN_NO_HT40;
/* Check if we do have HT40 channels */ if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 &&
il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) return 0;
/* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ for (band = 6; band <= 7; band++) { enum nl80211_band ieeeband;
/* EEPROM band 6 is 2.4, band 7 is 5 GHz */
ieeeband =
(band == 6) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
/* Loop through each band adding each of the channels */ for (ch = 0; ch < eeprom_ch_count; ch++) { /* Set up driver's info for lower half */
il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch],
&eeprom_ch_info[ch],
IEEE80211_CHAN_NO_HT40PLUS);
/* Set up driver's info for upper half */
il_mod_ht40_chan_info(il, ieeeband,
eeprom_ch_idx[ch] + 4,
&eeprom_ch_info[ch],
IEEE80211_CHAN_NO_HT40MINUS);
}
}
/* * Setting power level allows the card to go to sleep when not busy. * * We calculate a sleep command based on the required latency, which * we get from mac80211.
*/
/* Don't update the RX chain when chain noise calibration is running */
update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE ||
il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE;
if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) return 0;
if (!il_is_ready_rf(il)) return -EIO;
/* scan complete use sleep_power_next, need to be updated */
memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); if (test_bit(S_SCANNING, &il->status) && !force) {
D_INFO("Defer power set mode while scanning\n"); return 0;
}
if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)
set_bit(S_POWER_PMI, &il->status);
ret = il_set_power(il, cmd); if (!ret) { if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK))
clear_bit(S_POWER_PMI, &il->status);
if (il->ops->update_chain_flags && update_chains)
il->ops->update_chain_flags(il); elseif (il->ops->update_chain_flags)
D_POWER("Cannot update the power, chain noise " "calibration running: %d\n",
il->chain_noise_data.state);
memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd));
} else
IL_ERR("set power fail, ret = %d", ret);
return ret;
}
int
il_power_update_mode(struct il_priv *il, bool force)
{ struct il_powertable_cmd cmd;
/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after * sending probe req. This should be set long enough to hear probe responses
* from more than one AP. */ #define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ #define IL_ACTIVE_DWELL_TIME_52 (20)
/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. * Must be set longer than active dwell time.
* For the most reliable scan, set > AP beacon interval (typically 100msec). */ #define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ #define IL_PASSIVE_DWELL_TIME_52 (10) #define IL_PASSIVE_DWELL_BASE (100) #define IL_CHANNEL_TUNE_TIME 5
/* Exit instantly with error when device is not ready * to receive scan abort command or it does not perform
* hardware scan currently */ if (!test_bit(S_READY, &il->status) ||
!test_bit(S_GEO_CONFIGURED, &il->status) ||
!test_bit(S_SCAN_HW, &il->status) ||
test_bit(S_FW_ERROR, &il->status) ||
test_bit(S_EXIT_PENDING, &il->status)) return -EIO;
ret = il_send_cmd_sync(il, &cmd); if (ret) return ret;
pkt = (struct il_rx_pkt *)cmd.reply_page; if (pkt->u.status != CAN_ABORT_STATUS) { /* The scan abort will return 1 for success or * 2 for "failure". A failure condition can be * due to simply not being in an active scan which * can occur if we send the scan abort before we * the microcode has notified us that a scan is
* completed. */
D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status);
ret = -EIO;
}
/* check if scan was requested from mac80211 */ if (il->scan_request) {
D_SCAN("Complete scan in mac80211\n");
ieee80211_scan_completed(il->hw, &info);
}
staticvoid
il_do_scan_abort(struct il_priv *il)
{ int ret;
lockdep_assert_held(&il->mutex);
if (!test_bit(S_SCANNING, &il->status)) {
D_SCAN("Not performing scan to abort\n"); return;
}
if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) {
D_SCAN("Scan abort in progress\n"); return;
}
ret = il_send_scan_abort(il); if (ret) {
D_SCAN("Send scan abort failed %d\n", ret);
il_force_scan_end(il);
} else
D_SCAN("Successfully send scan abort\n");
}
/* * il_scan_cancel - Cancel any currently executing HW scan
*/ int
il_scan_cancel(struct il_priv *il)
{
D_SCAN("Queuing abort scan\n");
queue_work(il->workqueue, &il->abort_scan); return 0;
}
EXPORT_SYMBOL(il_scan_cancel);
/* * il_scan_cancel_timeout - Cancel any currently executing HW scan * @ms: amount of time to wait (in milliseconds) for scan to abort *
*/ int
il_scan_cancel_timeout(struct il_priv *il, unsignedlong ms)
{ unsignedlong timeout = jiffies + msecs_to_jiffies(ms);
lockdep_assert_held(&il->mutex);
D_SCAN("Scan cancel timeout\n");
il_do_scan_abort(il);
while (time_before_eq(jiffies, timeout)) { if (!test_bit(S_SCAN_HW, &il->status)) break;
msleep(20);
}
if (il_is_any_associated(il)) { /* * If we're associated, we clamp the maximum passive * dwell time to be 98% of the smallest beacon interval * (minus 2 * channel tune time)
*/
value = il->vif ? il->vif->bss_conf.beacon_int : 0; if (value > IL_PASSIVE_DWELL_BASE || !value)
value = IL_PASSIVE_DWELL_BASE;
value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2;
passive = min(value, passive);
}
/* Since we are here firmware does not finish scan and * most likely is in bad shape, so we don't bother to
* send abort command, just force scan complete to mac80211 */
mutex_lock(&il->mutex);
il_force_scan_end(il);
mutex_unlock(&il->mutex);
}
/* * il_fill_probe_req - fill in all required fields and IE for probe request
*/
u16
il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, const u8 *ta, const u8 *ies, int ie_len, int left)
{ int len = 0;
u8 *pos = NULL;
/* Make sure there is enough space for the probe request,
* two mandatory IEs and the data */
left -= 24; if (left < 0) return 0;
/* We keep scan_check work queued in case when firmware will not
* report back scan completed notification */
mutex_lock(&il->mutex);
il_scan_cancel_timeout(il, 200);
mutex_unlock(&il->mutex);
}
aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status); if (aborted)
D_SCAN("Aborted scan completed.\n");
if (!test_and_clear_bit(S_SCANNING, &il->status)) {
D_SCAN("Scan already completed.\n"); goto out_settings;
}
il_complete_scan(il, aborted);
out_settings: /* Can we still talk to firmware ? */ if (!il_is_ready_rf(il)) goto out;
/* * We do not commit power settings while scan is pending, * do it now if the settings changed.
*/
il_power_set_mode(il, &il->power_data.sleep_cmd_next, false);
il_set_tx_power(il, il->tx_power_next, false);
if (cancel_delayed_work_sync(&il->scan_check)) {
mutex_lock(&il->mutex);
il_force_scan_end(il);
mutex_unlock(&il->mutex);
}
}
EXPORT_SYMBOL(il_cancel_scan_deferred_work);
/* il->sta_lock must be held */ staticvoid
il_sta_ucode_activate(struct il_priv *il, u8 sta_id)
{
if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE))
IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n",
sta_id, il->stations[sta_id].sta.sta.addr);
if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) {
D_ASSOC("STA id %u addr %pM already present" " in uCode (according to driver)\n", sta_id,
il->stations[sta_id].sta.sta.addr);
} else {
il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE;
D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id,
il->stations[sta_id].sta.sta.addr);
}
}
if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags); return ret;
}
D_INFO("Processing response for adding station %u\n", sta_id);
spin_lock_irqsave(&il->sta_lock, flags);
switch (pkt->u.add_sta.status) { case ADD_STA_SUCCESS_MSK:
D_INFO("C_ADD_STA PASSED\n");
il_sta_ucode_activate(il, sta_id);
ret = 0; break; case ADD_STA_NO_ROOM_IN_TBL:
IL_ERR("Adding station %d failed, no room in table.\n", sta_id); break; case ADD_STA_NO_BLOCK_ACK_RESOURCE:
IL_ERR("Adding station %d failed, no block ack resource.\n",
sta_id); break; case ADD_STA_MODIFY_NON_EXIST_STA:
IL_ERR("Attempting to modify non-existing station %d\n",
sta_id); break; default:
D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status); break;
}
D_INFO("%s station id %u addr %pM\n",
il->stations[sta_id].sta.mode ==
STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id,
il->stations[sta_id].sta.sta.addr);
/* * XXX: The MAC address in the command buffer is often changed from * the original sent to the device. That is, the MAC address * written to the command buffer often is not the same MAC address * read from the command buffer when the command returns. This * issue has not yet been resolved and this debugging is left to * observe the problem.
*/
D_INFO("%s station according to cmd buffer %pM\n",
il->stations[sta_id].sta.mode ==
STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr);
spin_unlock_irqrestore(&il->sta_lock, flags);
/* * il_prep_station - Prepare station information for addition * * should be called with sta_lock held
*/
u8
il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
{ struct il_station_entry *station; int i;
u8 sta_id = IL_INVALID_STATION;
u16 rate;
if (is_ap)
sta_id = IL_AP_ID; elseif (is_broadcast_ether_addr(addr))
sta_id = il->hw_params.bcast_id; else for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { if (ether_addr_equal(il->stations[i].sta.sta.addr,
addr)) {
sta_id = i; break;
}
if (!il->stations[i].used &&
sta_id == IL_INVALID_STATION)
sta_id = i;
}
/* * These two conditions have the same outcome, but keep them * separate
*/ if (unlikely(sta_id == IL_INVALID_STATION)) return sta_id;
/* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another.
*/ if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
D_INFO("STA %d already in process of being added.\n", sta_id); return sta_id;
}
station = &il->stations[sta_id];
station->used = IL_STA_DRIVER_ACTIVE;
D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr);
il->num_stations++;
/* Set up the C_ADD_STA command to send to device */
memset(&station->sta, 0, sizeof(struct il_addsta_cmd));
memcpy(station->sta.sta.addr, addr, ETH_ALEN);
station->sta.mode = 0;
station->sta.sta.sta_id = sta_id;
station->sta.station_flags = 0;
/* * OK to call unconditionally, since local stations (IBSS BSSID * STA and broadcast STA) pass in a NULL sta, and mac80211 * doesn't allow HT IBSS.
*/
il_set_ht_add_station(il, sta_id, sta);
/* 3945 only */
rate = (il->band == NL80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP; /* Turn on both antennas for the station... */
station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
*sta_id_r = 0;
spin_lock_irqsave(&il->sta_lock, flags_spin);
sta_id = il_prep_station(il, addr, is_ap, sta); if (sta_id == IL_INVALID_STATION) {
IL_ERR("Unable to prepare station %pM for addition\n", addr);
spin_unlock_irqrestore(&il->sta_lock, flags_spin); return -EINVAL;
}
/* * uCode is not able to deal with multiple requests to add a * station. Keep track if one is in progress so that we do not send * another.
*/ if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) {
D_INFO("STA %d already in process of being added.\n", sta_id);
spin_unlock_irqrestore(&il->sta_lock, flags_spin); return -EEXIST;
}
/* Add station to device's station table */
ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); if (ret) {
spin_lock_irqsave(&il->sta_lock, flags_spin);
IL_ERR("Adding station %pM failed.\n",
il->stations[sta_id].sta.sta.addr);
il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS;
spin_unlock_irqrestore(&il->sta_lock, flags_spin);
}
*sta_id_r = sta_id; return ret;
}
EXPORT_SYMBOL(il_add_station_common);
/* * il_sta_ucode_deactivate - deactivate ucode status for a station * * il->sta_lock must be held
*/ staticvoid
il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id)
{ /* Ucode must be active and driver must be non active */ if ((il->stations[sta_id].
used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) !=
IL_STA_UCODE_ACTIVE)
IL_ERR("removed non active STA %u\n", sta_id);
if (!il_is_ready(il)) {
D_INFO("Unable to remove station %pM, device not ready.\n",
addr); /* * It is typical for stations to be removed when we are * going down. Return success since device will be down * soon anyway
*/ return 0;
}
D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr);
if (WARN_ON(sta_id == IL_INVALID_STATION)) return -EINVAL;
spin_lock_irqsave(&il->sta_lock, flags);
if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) {
D_INFO("Removing %pM but non DRIVER active\n", addr); goto out_err;
}
if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) {
D_INFO("Removing %pM but non UCODE active\n", addr); goto out_err;
}
if (il->stations[sta_id].used & IL_STA_LOCAL) {
kfree(il->stations[sta_id].lq);
il->stations[sta_id].lq = NULL;
}
/* * il_clear_ucode_stations - clear ucode station table bits * * This function clears all the bits in the driver indicating * which stations are active in the ucode. Call when something * other than explicit station management would cause this in * the ucode, e.g. unassociated RXON.
*/ void
il_clear_ucode_stations(struct il_priv *il)
{ int i; unsignedlong flags_spin; bool cleared = false;
D_INFO("Clearing ucode stations in driver\n");
spin_lock_irqsave(&il->sta_lock, flags_spin); for (i = 0; i < il->hw_params.max_stations; i++) { if (il->stations[i].used & IL_STA_UCODE_ACTIVE) {
D_INFO("Clearing ucode active for station %d\n", i);
il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
cleared = true;
}
}
spin_unlock_irqrestore(&il->sta_lock, flags_spin);
if (!cleared)
D_INFO("No active stations found to be cleared\n");
}
EXPORT_SYMBOL(il_clear_ucode_stations);
/* * il_restore_stations() - Restore driver known stations to device * * All stations considered active by driver, but not present in ucode, is * restored. * * Function sleeps.
*/ void
il_restore_stations(struct il_priv *il)
{ struct il_addsta_cmd sta_cmd; struct il_link_quality_cmd lq; unsignedlong flags_spin; int i; bool found = false; int ret; bool send_lq;
if (!il_is_ready(il)) {
D_INFO("Not ready yet, not restoring any stations.\n"); return;
}
D_ASSOC("Restoring all known stations ... start.\n");
spin_lock_irqsave(&il->sta_lock, flags_spin); for (i = 0; i < il->hw_params.max_stations; i++) { if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) &&
!(il->stations[i].used & IL_STA_UCODE_ACTIVE)) {
D_ASSOC("Restoring sta %pM\n",
il->stations[i].sta.sta.addr);
il->stations[i].sta.mode = 0;
il->stations[i].used |= IL_STA_UCODE_INPROGRESS;
found = true;
}
}
for (i = 0; i < il->hw_params.max_stations; i++) { if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) {
memcpy(&sta_cmd, &il->stations[i].sta, sizeof(struct il_addsta_cmd));
send_lq = false; if (il->stations[i].lq) {
memcpy(&lq, il->stations[i].lq, sizeof(struct il_link_quality_cmd));
send_lq = true;
}
spin_unlock_irqrestore(&il->sta_lock, flags_spin);
ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); if (ret) {
spin_lock_irqsave(&il->sta_lock, flags_spin);
IL_ERR("Adding station %pM failed.\n",
il->stations[i].sta.sta.addr);
il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE;
il->stations[i].used &=
~IL_STA_UCODE_INPROGRESS;
spin_unlock_irqrestore(&il->sta_lock,
flags_spin);
} /* * Rate scaling has already been initialized, send * current LQ command
*/ if (send_lq)
il_send_lq_cmd(il, &lq, CMD_SYNC, true);
spin_lock_irqsave(&il->sta_lock, flags_spin);
il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS;
}
}
spin_unlock_irqrestore(&il->sta_lock, flags_spin); if (!found)
D_INFO("Restoring all known stations" " .... no stations to be restored.\n"); else
D_INFO("Restoring all known stations"" .... complete.\n");
}
EXPORT_SYMBOL(il_restore_stations);
int
il_get_free_ucode_key_idx(struct il_priv *il)
{ int i;
for (i = 0; i < il->sta_key_max_num; i++) if (!test_and_set_bit(i, &il->ucode_key_table)) return i;
#ifdef CONFIG_IWLEGACY_DEBUG staticvoid
il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
{ int i;
D_RATE("lq station id 0x%x\n", lq->sta_id);
D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk,
lq->general_params.dual_stream_ant_msk);
for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags);
} #else staticinlinevoid
il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq)
{
} #endif
/* * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity * * It sometimes happens when a HT rate has been in use and we * loose connectivity with AP then mac80211 will first tell us that the * current channel is not HT anymore before removing the station. In such a * scenario the RXON flags will be updated to indicate we are not * communicating HT anymore, but the LQ command may still contain HT rates. * Test for this to prevent driver from sending LQ command between the time * RXON flags are updated and when LQ command is updated.
*/ staticbool
il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq)
{ int i;
if (il->ht.enabled) returntrue;
D_INFO("Channel %u is not an HT channel\n", il->active.channel); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) {
D_INFO("idx %d of LQ expects HT channel\n", i); returnfalse;
}
} returntrue;
}
/* * il_send_lq_cmd() - Send link quality command * @init: This command is sent as part of station initialization right * after station has been added. * * The link quality command is sent as the last step of station creation. * This is the special case in which init is set and we call a callback in * this case to clear the state indicating that station creation is in * progress.
*/ int
il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq,
u8 flags, bool init)
{ int ret = 0; unsignedlong flags_spin;
mutex_lock(&il->mutex);
D_MAC80211("enter station %pM\n", sta->addr);
ret = il_remove_station(il, sta_common->sta_id, sta->addr); if (ret)
IL_ERR("Error removing station %pM\n", sta->addr);
D_MAC80211("leave ret %d\n", ret);
mutex_unlock(&il->mutex);
return ret;
}
EXPORT_SYMBOL(il_mac_sta_remove);
/************************** RX-FUNCTIONS ****************************/ /* * Rx theory of operation * * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), * each of which point to Receive Buffers to be filled by the NIC. These get * used not only for Rx frames, but for any command response or notification * from the NIC. The driver and NIC manage the Rx buffers by means * of idxes into the circular buffer. * * Rx Queue Indexes * The host/firmware share two idx registers for managing the Rx buffers. * * The READ idx maps to the first position that the firmware may be writing * to -- the driver can read up to (but not including) this position and get * good data. * The READ idx is managed by the firmware once the card is enabled. * * The WRITE idx maps to the last position the driver has read from -- the * position preceding WRITE is the last slot the firmware can place a packet. * * The queue is empty (no good data) if WRITE = READ - 1, and is full if * WRITE = READ. * * During initialization, the host sets up the READ queue position to the first * IDX position, and WRITE to the last (READ - 1 wrapped) * * When the firmware places a packet in a buffer, it will advance the READ idx * and fire the RX interrupt. The driver can then query the READ idx and * process as many packets as possible, moving the WRITE idx forward as it * resets the Rx queue buffers with new memory. * * The management in the driver is as follows: * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled * to replenish the iwl->rxq->rx_free. * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the * iwl->rxq is replenished and the READ IDX is updated (updating the * 'processed' and 'read' driver idxes as well) * + A received packet is processed and handed to the kernel network stack, * detached from the iwl->rxq. The driver 'processed' idx is updated. * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ * IDX is not incremented and iwl->status(RX_STALLED) is set. If there * were enough free buffers and RX_STALLED is set it is cleared. * * * Driver sequence: * * il_rx_queue_alloc() Allocates rx_free * il_rx_replenish() Replenishes rx_free list from rx_used, and calls * il_rx_queue_restock * il_rx_queue_restock() Moves available buffers from rx_free into Rx * queue, updates firmware pointers, and updates * the WRITE idx. If insufficient rx_free buffers * are available, schedules il_rx_replenish * * -- enable interrupts -- * ISR - il_rx() Detach il_rx_bufs from pool up to the * READ IDX, detaching the SKB from the pool. * Moves the packet buffer from queue to rx_used. * Calls il_rx_queue_restock to refill any empty * slots. * ... *
*/
/* * il_rx_queue_space - Return number of free slots available in queue.
*/ int
il_rx_queue_space(conststruct il_rx_queue *q)
{ int s = q->read - q->write; if (s <= 0)
s += RX_QUEUE_SIZE; /* keep some buffer to not confuse full and empty queue */
s -= 2; if (s < 0)
s = 0; return s;
}
EXPORT_SYMBOL(il_rx_queue_space);
/* * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue
*/ void
il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q)
{ unsignedlong flags;
u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg;
u32 reg;
spin_lock_irqsave(&q->lock, flags);
if (q->need_update == 0) goto exit_unlock;
/* If power-saving is in use, make sure device is awake */ if (test_bit(S_POWER_PMI, &il->status)) {
reg = _il_rd(il, CSR_UCODE_DRV_GP1);
/* Fill the rx_used queue with _all_ of the Rx buffers */ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
/* Set us so that we have processed and used all buffers, but have
* not restocked the Rx queue with fresh buffers */
rxq->read = rxq->write = 0;
rxq->write_actual = 0;
rxq->free_count = 0;
rxq->need_update = 0; return 0;
/* * returns non-zero if packet should be dropped
*/ int
il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
u32 decrypt_res, struct ieee80211_rx_status *stats)
{
u16 fc = le16_to_cpu(hdr->frame_control);
/* * All contexts have the same setting here due to it being * a module parameter, so OK to check any context.
*/ if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) return 0;
if (!(fc & IEEE80211_FCTL_PROTECTED)) return 0;
D_RX("decrypt_res:0x%x\n", decrypt_res); switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { case RX_RES_STATUS_SEC_TYPE_TKIP: /* The uCode has got a bad phase 1 Key, pushes the packet.
* Decryption will be done in SW. */ if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
RX_RES_STATUS_BAD_KEY_TTAK) break;
fallthrough;
case RX_RES_STATUS_SEC_TYPE_WEP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
RX_RES_STATUS_BAD_ICV_MIC) { /* bad ICV, the packet is destroyed since the
* decryption is inplace, drop it */
D_RX("Packet destroyed\n"); return -1;
}
fallthrough; case RX_RES_STATUS_SEC_TYPE_CCMP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
RX_RES_STATUS_DECRYPT_OK) {
D_RX("hw decrypt successfully!!!\n");
stats->flag |= RX_FLAG_DECRYPTED;
} break;
/* * il_txq_update_write_ptr - Send new write idx to hardware
*/ void
il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq)
{
u32 reg = 0; int txq_id = txq->q.id;
if (txq->need_update == 0) return;
/* if we're trying to save power */ if (test_bit(S_POWER_PMI, &il->status)) { /* wake up nic if it's powered down ... * uCode will wake up, and interrupt us again, so next
* time we'll skip this part. */
reg = _il_rd(il, CSR_UCODE_DRV_GP1);
/* * else not in power-save mode, * uCode will never sleep when we're * trying to tx (during RFKILL, we're not trying to tx).
*/
} else
_il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
txq->need_update = 0;
}
EXPORT_SYMBOL(il_txq_update_write_ptr);
/* * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's
*/ void
il_tx_queue_unmap(struct il_priv *il, int txq_id)
{ struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q;
/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** * DMA services * * Theory of operation * * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer * of buffer descriptors, each of which points to one or more data buffers for * the device to read from or fill. Driver and device exchange status of each * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty * entries in each circular buffer, to protect against confusing empty and full * queue states. * * The device reads or writes the data in the queues via the device's several * DMA/FIFO channels. Each queue is mapped to a single DMA channel. * * For Tx queue, there are low mark and high mark limits. If, after queuing * the packet for Tx, free space become < low mark, Tx queue stopped. When * reclaiming packets (on 'tx done IRQ), if free space become > high mark, * Tx queue resumed. * * See more detailed info in 4965.h.
***************************************************/
int
il_queue_space(conststruct il_queue *q)
{ int s = q->read_ptr - q->write_ptr;
if (q->read_ptr > q->write_ptr)
s -= q->n_bd;
if (s <= 0)
s += q->n_win; /* keep some reserve to not confuse empty and full situations */
s -= 2; if (s < 0)
s = 0; return s;
}
EXPORT_SYMBOL(il_queue_space);
/* * il_queue_init - Initialize queue's high/low-water and read/write idxes
*/ staticint
il_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id)
{ /* * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * il_queue_inc_wrap and il_queue_dec_wrap are broken.
*/
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); /* FIXME: remove q->n_bd */
q->n_bd = TFD_QUEUE_SIZE_MAX;
q->n_win = slots;
q->id = id;
/* slots_must be power-of-two size, otherwise
* il_get_cmd_idx is broken. */
BUG_ON(!is_power_of_2(slots));
/* * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue
*/ staticint
il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id)
{ struct device *dev = &il->pci_dev->dev;
size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX;
/* Driver ilate data, only for Tx (not command) queues,
* not shared with device. */ if (id != il->cmd_queue) {
txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, sizeof(struct sk_buff *),
GFP_KERNEL); if (!txq->skbs) {
IL_ERR("Fail to alloc skbs\n"); goto error;
}
} else
txq->skbs = NULL;
/* Circular buffer of transmit frame descriptors (TFDs),
* shared with device */
txq->tfds =
dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); if (!txq->tfds) goto error;
txq->q.id = id;
return 0;
error:
kfree(txq->skbs);
txq->skbs = NULL;
return -ENOMEM;
}
/* * il_tx_queue_init - Allocate and initialize one tx/cmd queue
*/ int
il_tx_queue_init(struct il_priv *il, u32 txq_id)
{ int i, len, ret; int slots, actual_slots; struct il_tx_queue *txq = &il->txq[txq_id];
/* * Alloc buffer array for commands (Tx or other types of commands). * For the command queue (#4/#9), allocate command space + one big * command for scan, since scan command is very huge; the system will * not have two scans at the same time, so only one is needed. * For normal Tx queues (all other queues), no super-size command * space is needed.
*/ if (txq_id == il->cmd_queue) {
slots = TFD_CMD_SLOTS;
actual_slots = slots + 1;
} else {
slots = TFD_TX_CMD_SLOTS;
actual_slots = slots;
}
if (!txq->meta || !txq->cmd) goto out_free_arrays;
len = sizeof(struct il_device_cmd); for (i = 0; i < actual_slots; i++) { /* only happens for cmd queue */ if (i == slots)
len = IL_MAX_CMD_SIZE;
txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) goto err;
}
/* Alloc driver data array and TFD circular buffer */
ret = il_tx_queue_alloc(il, txq, txq_id); if (ret) goto err;
txq->need_update = 0;
/* * For the default queues 0-3, set up the swq_id * already -- all others need to get one later * (if they need one at all).
*/ if (txq_id < 4)
il_set_swq_id(txq, txq_id, txq_id);
/* * il_enqueue_hcmd - enqueue a uCode command * @il: device ilate data point * @cmd: a point to the ucode command structure * * The function returns < 0 values to indicate the operation is * failed. On success, it turns the idx (> 0) of command in the * command queue.
*/ int
il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd)
{ struct il_tx_queue *txq = &il->txq[il->cmd_queue]; struct il_queue *q = &txq->q; struct il_device_cmd *out_cmd; struct il_cmd_meta *out_meta;
dma_addr_t phys_addr; unsignedlong flags;
u8 *out_payload;
u32 idx;
u16 fix_size;
/* If any of the command structures end up being larger than * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then * we will need to increase the size of the TFD entries * Also, check to see if command buffer should not exceed the size
* of device_cmd and max_cmd_size. */
BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
!(cmd->flags & CMD_SIZE_HUGE));
BUG_ON(fix_size > IL_MAX_CMD_SIZE);
/* The payload is in the same place in regular and huge * command buffers, but we need to let the compiler know when * we're using a larger payload buffer to avoid "field- * spanning write" warnings at run-time for huge commands.
*/ if (cmd->flags & CMD_SIZE_HUGE)
out_payload = ((struct il_device_cmd_huge *)out_cmd)->cmd.payload; else
out_payload = out_cmd->cmd.payload;
if (WARN_ON(out_meta->flags & CMD_MAPPED)) {
spin_unlock_irqrestore(&il->hcmd_lock, flags); return -ENOSPC;
}
memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */
out_meta->flags = cmd->flags | CMD_MAPPED; if (cmd->flags & CMD_WANT_SKB)
out_meta->source = cmd; if (cmd->flags & CMD_ASYNC)
out_meta->callback = cmd->callback;
/* * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd * * When FW advances 'R' idx, all entries between old and new 'R' idx * need to be reclaimed. As result, some free space forms. If there is * enough free space (> low mark), wake the stack that feeds us.
*/ staticvoid
il_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx)
{ struct il_tx_queue *txq = &il->txq[txq_id]; struct il_queue *q = &txq->q; int nfreed = 0;
if (idx >= q->n_bd || il_queue_used(q, idx) == 0) {
IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd,
q->write_ptr, q->read_ptr); return;
}
/* * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim * * If an Rx buffer has an async callback associated with it the callback * will be executed. The attached skb (if present) will only be freed * if the callback returns 1
*/ void
il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb)
{ struct il_rx_pkt *pkt = rxb_addr(rxb);
u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); int idx = SEQ_TO_IDX(sequence); int cmd_idx; bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); struct il_device_cmd *cmd; struct il_cmd_meta *meta; struct il_tx_queue *txq = &il->txq[il->cmd_queue]; unsignedlong flags;
/* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced
* in the queue management code. */ if (WARN
(txq_id != il->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n",
txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr,
il->txq[il->cmd_queue].q.write_ptr)) {
il_print_hex_error(il, pkt, 32); return;
}
MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965");
MODULE_VERSION(IWLWIFI_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
/* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the * priority line in the PCIx). * set bt_coex_active to false, uCode will ignore the BT activity and * perform the normal operation * * User might experience transmit issue on some platform due to WiFi/BT * co-exist problem. The possible behaviors are: * Able to scan and finding all the available AP * Not able to associate with any AP * On those platforms, WiFi communication can be restored by set * "bt_coex_active" module parameter to "false" * * default: bt_coex_active = true (BT_COEX_ENABLE)
*/ staticbool bt_coex_active = true;
module_param(bt_coex_active, bool, 0444);
MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist");
/* * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 * the bit will not set if it is pure 40MHz case
*/ if (ht_cap && !ht_cap->ht_supported) returnfalse;
#ifdef CONFIG_IWLEGACY_DEBUGFS if (il->disable_ht40) returnfalse; #endif
/* * If mac80211 hasn't given us a beacon interval, program * the default into the device.
*/ if (!beacon_val) return DEFAULT_BEACON_INTERVAL;
/* * If the beacon interval we obtained from the peer * is too large, we'll have to wake up more often * (and in IBSS case, we'll beacon too much) * * For example, if max_beacon_val is 4096, and the * requested beacon interval is 7000, we'll have to * use 3500 to be able to wake up on the beacons. * * This could badly influence beacon detection stats.
*/
/* * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed * @il: staging_rxon is compared to active_rxon * * If the RXON structure is changing enough to require a new tune, * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
*/ int
il_full_rxon_required(struct il_priv *il)
{ conststruct il_rxon_cmd *staging = &il->staging; conststruct il_rxon_cmd *active = &il->active;
#define CHK(cond) \ if ((cond)) { \
D_INFO("need full RXON - "#cond"\n"); \ return 1; \
}
/* These items are only settable from the full RXON command */
CHK(!il_is_associated(il));
CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr));
CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr));
CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr,
active->wlap_bssid_addr));
CHK_NEQ(staging->dev_type, active->dev_type);
CHK_NEQ(staging->channel, active->channel);
CHK_NEQ(staging->air_propagation, active->air_propagation);
CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
active->ofdm_ht_single_stream_basic_rates);
CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
active->ofdm_ht_dual_stream_basic_rates);
CHK_NEQ(staging->assoc_id, active->assoc_id);
/* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can * be updated with the RXON_ASSOC command -- however only some
* flag transitions are allowed using RXON_ASSOC */
/* Check if we are not switching bands */
CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
active->flags & RXON_FLG_BAND_24G_MSK);
/* Check if we are switching association toggle */
CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
active->filter_flags & RXON_FILTER_ASSOC_MSK);
#undef CHK #undef CHK_NEQ
return 0;
}
EXPORT_SYMBOL(il_full_rxon_required);
u8
il_get_lowest_plcp(struct il_priv *il)
{ /* * Assign the lowest rate -- should really get this from * the beacon skb from mac80211.
*/ if (il->staging.flags & RXON_FLG_BAND_24G_MSK) return RATE_1M_PLCP; else return RATE_6M_PLCP;
}
EXPORT_SYMBOL(il_get_lowest_plcp);
/* * il_set_rxon_channel - Set the band and channel values in staging RXON * @ch: requested channel as a pointer to struct ieee80211_channel
* NOTE: Does not commit to the hardware; it sets appropriate bit fields * in the staging RXON flag structure based on the ch->band
*/ int
il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch)
{ enum nl80211_band band = ch->band;
u16 channel = ch->hw_value;
if (le16_to_cpu(il->staging.channel) == channel && il->band == band) return 0;
/* * initialize rxon structure with default values from eeprom
*/ void
il_connection_init_rx_config(struct il_priv *il)
{ conststruct il_channel_info *ch_info;
memset(&il->staging, 0, sizeof(il->staging));
switch (il->iw_mode) { case NL80211_IFTYPE_UNSPECIFIED:
il->staging.dev_type = RXON_DEV_TYPE_ESS; break; case NL80211_IFTYPE_STATION:
il->staging.dev_type = RXON_DEV_TYPE_ESS;
il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; break; case NL80211_IFTYPE_ADHOC:
il->staging.dev_type = RXON_DEV_TYPE_IBSS;
il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
il->staging.filter_flags =
RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; break; default:
IL_ERR("Unsupported interface type %d\n", il->vif->type); return;
}
#if 0 /* TODO: Figure out when short_preamble would be set and cache from
* that */ if (!hw_to_local(il->hw)->short_preamble)
il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; else
il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; #endif
/* clear both MIX and PURE40 mode flag */
il->staging.flags &=
~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); if (il->vif)
memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN);
/* * Start up NIC's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via il_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor
*/ int
il_apm_init(struct il_priv *il)
{ int ret = 0;
u16 lctl;
D_INFO("Init card's basic functions\n");
/* * Use "set_bit" below rather than "write", to preserve any hardware * bits already set by default after reset.
*/
/* * Disable L0s without affecting L1; * don't wait for ICH L0s (ICH bug W/A)
*/
il_set_bit(il, CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
/* Set FH wait threshold to maximum (HW error during stress W/A) */
il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
/* * Enable HAP INTA (interrupt from management bus) to * wake device's PCI Express link L1a -> L0s * NOTE: This is no-op for 3945 (non-existent bit)
*/
il_set_bit(il, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
/* * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. * Check if BIOS (or OS) enabled L1-ASPM on this device. * If so (likely), disable L0S, so device moves directly L0->L1; * costs negligible amount of power savings. * If not (unlikely), enable L0S, so there is at least some * power savings, even without L1.
*/ if (il->cfg->set_l0s) {
ret = pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); if (!ret && (lctl & PCI_EXP_LNKCTL_ASPM_L1)) { /* L1-ASPM enabled; disable(!) L0S */
il_set_bit(il, CSR_GIO_REG,
CSR_GIO_REG_VAL_L0S_ENABLED);
D_POWER("L1 Enabled; Disabling L0S\n");
} else { /* L1-ASPM disabled; enable(!) L0S */
il_clear_bit(il, CSR_GIO_REG,
CSR_GIO_REG_VAL_L0S_ENABLED);
D_POWER("L1 Disabled; Enabling L0S\n");
}
}
/* Configure analog phase-lock-loop before activating to D0A */ if (il->cfg->pll_cfg_val)
il_set_bit(il, CSR_ANA_PLL_CFG,
il->cfg->pll_cfg_val);
/* * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state.
*/
il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* * Wait for clock stabilization; once stabilized, access to * device-internal resources is supported, e.g. il_wr_prph() * and accesses to uCode SRAM.
*/
ret =
_il_poll_bit(il, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) {
D_INFO("Failed to init the card\n"); goto out;
}
/* * Enable DMA and BSM (if used) clocks, wait for them to stabilize. * BSM (Boostrap State Machine) is only in 3945 and 4965. * * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits * do not disable clocks. This preserves any hardware bits already * set by default in "CLK_CTRL_REG" after reset.
*/ if (il->cfg->use_bsm)
il_wr_prph(il, APMG_CLK_EN_REG,
APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); else
il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
udelay(20);
int
il_set_tx_power(struct il_priv *il, s8 tx_power, bool force)
{ int ret;
s8 prev_tx_power; bool defer;
lockdep_assert_held(&il->mutex);
if (il->tx_power_user_lmt == tx_power && !force) return 0;
if (!il->ops->send_tx_power) return -EOPNOTSUPP;
/* 0 dBm mean 1 milliwatt */ if (tx_power < 0) {
IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power); return -EINVAL;
}
if (tx_power > il->tx_power_device_lmt) {
IL_WARN("Requested user TXPOWER %d above upper limit %d.\n",
tx_power, il->tx_power_device_lmt); return -EINVAL;
}
if (!il_is_ready_rf(il)) return -EIO;
/* scan complete and commit_rxon use tx_power_next value,
* it always need to be updated for newest request */
il->tx_power_next = tx_power;
/* do not set tx power when scanning or channel changing */
defer = test_bit(S_SCANNING, &il->status) ||
memcmp(&il->active, &il->staging, sizeof(il->staging)); if (defer && !force) {
D_INFO("Deferring tx power set\n"); return 0;
}
/* if fail to set tx_power, restore the orig. tx power */ if (ret) {
il->tx_power_user_lmt = prev_tx_power;
il->tx_power_next = prev_tx_power;
} return ret;
}
EXPORT_SYMBOL(il_set_tx_power);
if (il->ops->set_rxon_chain)
il->ops->set_rxon_chain(il);
return il_commit_rxon(il);
}
int
il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{ struct il_priv *il = hw->priv; int err; bool reset;
mutex_lock(&il->mutex);
D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr);
if (!il_is_ready_rf(il)) {
IL_WARN("Try to add interface when device not ready\n");
err = -EINVAL; goto out;
}
/* * We do not support multiple virtual interfaces, but on hardware reset * we have to add the same interface again.
*/
reset = (il->vif == vif); if (il->vif && !reset) {
err = -EOPNOTSUPP; goto out;
}
il->vif = vif;
il->iw_mode = vif->type;
err = il_set_mode(il); if (err) {
IL_WARN("Fail to set mode %d\n", vif->type); if (!reset) {
il->vif = NULL;
il->iw_mode = NL80211_IFTYPE_STATION;
}
}
/* * if the request is from external(ex: debugfs), * then always perform the request in regardless the module * parameter setting * if the request is from internal (uCode error or driver * detect failure), then fw_restart module parameter * need to be check before performing firmware reload
*/
if (!external && !il->cfg->mod_params->restart_fw) {
D_INFO("Cancel firmware reload based on " "module parameter setting\n"); return 0;
}
IL_ERR("On demand firmware reload\n");
/* Set the FW error flag -- cleared on il_down */
set_bit(S_FW_ERROR, &il->status);
wake_up(&il->wait_command_queue); /* * Keep the restart process from trying to send host * commands by clearing the INIT status bit
*/
clear_bit(S_READY, &il->status);
queue_work(il->workqueue, &il->restart);
return 0;
}
EXPORT_SYMBOL(il_force_reset);
int
il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p)
{ struct il_priv *il = hw->priv; int err;
if (!il->vif || !il_is_ready_rf(il)) { /* * Huh? But wait ... this can maybe happen when * we're in the middle of a firmware restart!
*/
err = -EBUSY; goto out;
}
/* * On every watchdog tick we check (latest) time stamp. If it does not * change during timeout period and queue is not empty we reset firmware.
*/ staticint
il_check_stuck_queue(struct il_priv *il, int cnt)
{ struct il_tx_queue *txq = &il->txq[cnt]; struct il_queue *q = &txq->q; unsignedlong timeout; unsignedlong now = jiffies; int ret;
if (time_after(now, timeout)) {
IL_ERR("Queue %d stuck for %u ms.\n", q->id,
jiffies_to_msecs(now - txq->time_stamp));
ret = il_force_reset(il, false); return (ret == -EAGAIN) ? 0 : 1;
}
return 0;
}
/* * Making watchdog tick be a quarter of timeout assure we will * discover the queue hung between timeout and 1.25*timeout
*/ #define IL_WD_TICK(timeout) ((timeout) / 4)
/* * Watchdog timer callback, we check each tx queue for stuck, if hung * we reset the firmware. If everything is fine just rearm the timer.
*/ void
il_bg_watchdog(struct timer_list *t)
{ struct il_priv *il = timer_container_of(il, t, watchdog); int cnt; unsignedlong timeout;
if (test_bit(S_EXIT_PENDING, &il->status)) return;
timeout = il->cfg->wd_timeout; if (timeout == 0) return;
/* monitor and check for stuck cmd queue */ if (il_check_stuck_queue(il, il->cmd_queue)) return;
/* monitor and check for other stuck queues */ for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { /* skip as we already checked the command queue */ if (cnt == il->cmd_queue) continue; if (il_check_stuck_queue(il, cnt)) return;
}
if (timeout)
mod_timer(&il->watchdog,
jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); else
timer_delete(&il->watchdog);
}
EXPORT_SYMBOL(il_setup_watchdog);
/* * extended beacon time format * time in usec will be changed into a 32-bit value in extended:internal format * the extended part is the beacon counts * the internal part is the time in usec within one beacon interval
*/
u32
il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval)
{
u32 quot;
u32 rem;
u32 interval = beacon_interval * TIME_UNIT;
/* base is usually what we get from ucode with each received frame, * the same as HW timer counter counting down
*/
__le32
il_add_beacon_time(struct il_priv *il, u32 base, u32 addon,
u32 beacon_interval)
{
u32 base_low = base & il_beacon_time_mask_low(il,
il->hw_params.
beacon_time_tsf_bits);
u32 addon_low = addon & il_beacon_time_mask_low(il,
il->hw_params.
beacon_time_tsf_bits);
u32 interval = beacon_interval * TIME_UNIT;
u32 res = (base & il_beacon_time_mask_high(il,
il->hw_params.
beacon_time_tsf_bits)) +
(addon & il_beacon_time_mask_high(il,
il->hw_params.
beacon_time_tsf_bits));
if (base_low > addon_low)
res += base_low - addon_low; elseif (base_low < addon_low) {
res += interval + base_low - addon_low;
res += (1 << il->hw_params.beacon_time_tsf_bits);
} else
res += (1 << il->hw_params.beacon_time_tsf_bits);
/* * This function is called when system goes into suspend state * mac80211 will call il_mac_stop() from the mac80211 suspend function * first but since il_mac_stop() has no knowledge of who the caller is, * it will not call apm_ops.stop() to stop the DMA operation. * Calling apm_ops.stop here to make sure we stop the DMA.
*/
il_apm_stop(il);
/* * We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state.
*/
pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
if (unlikely(test_bit(S_SCANNING, &il->status))) {
scan_active = 1;
D_MAC80211("scan active\n");
}
if (changed &
(IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { /* mac80211 uses static for non-HT which is what we want */
il->current_ht_config.smps = conf->smps_mode;
/* * Recalculate chain counts. * * If monitor mode is enabled then mac80211 will * set up the SM PS mode to OFF if an HT channel is * configured.
*/ if (il->ops->set_rxon_chain)
il->ops->set_rxon_chain(il);
}
/* during scanning mac80211 will delay channel setting until * scan finish with changed = 0
*/ if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
/* * Default to no protection. Protection mode will * later be set from BSS config in il_ht_conf
*/
il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
/* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not
* support ht */ if ((le16_to_cpu(il->staging.channel) != ch))
il->staging.flags = 0;
if (il->ops->update_bcast_stations)
ret = il->ops->update_bcast_stations(il);
set_ch_out: /* The list of supported rates and rate mask can be different * for each band; since the band may have changed, reset
* the rate mask to what mac80211 lists */
il_set_rate(il);
}
if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) {
il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS); if (!il->power_data.ps_disabled)
IL_WARN_ONCE("Enabling power save might cause firmware crashes\n");
ret = il_power_update_mode(il, false); if (ret)
D_MAC80211("Error setting sleep level\n");
}
if (changed & IEEE80211_CONF_CHANGE_POWER) {
D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt,
conf->power_level);
il_set_tx_power(il, conf->power_level, false);
}
if (!il_is_ready(il)) {
D_MAC80211("leave - not ready\n"); goto out;
}
if (scan_active) goto out;
if (memcmp(&il->active, &il->staging, sizeof(il->staging)))
il_commit_rxon(il); else
D_INFO("Not re-sending same RXON configuration.\n"); if (ht_changed)
il_update_qos(il);
out:
D_MAC80211("leave ret %d\n", ret);
mutex_unlock(&il->mutex);
switch (vif->type) { case NL80211_IFTYPE_STATION:
rcu_read_lock();
sta = ieee80211_find_sta(vif, bss_conf->bssid); if (sta) { struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; int maxstreams;
if (ht_cap->mcs.rx_mask[1] == 0 &&
ht_cap->mcs.rx_mask[2] == 0)
ht_conf->single_chain_sufficient = true; if (maxstreams <= 1)
ht_conf->single_chain_sufficient = true;
} else { /* * If at all, this can only happen through a race * when the AP disconnects us while we're still * setting up the connection, in that case mac80211 * will soon tell us about that.
*/
ht_conf->single_chain_sufficient = true;
}
rcu_read_unlock(); break; case NL80211_IFTYPE_ADHOC:
ht_conf->single_chain_sufficient = true; break; default: break;
}
D_ASSOC("leave\n");
}
staticinlinevoid
il_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif)
{ /* * inform the ucode that there is no longer an * association and that no more packets should be * sent
*/
il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
il->staging.assoc_id = 0;
il_commit_rxon(il);
}
if (changes & BSS_CHANGED_BEACON_ENABLED) { /* FIXME: can we remove beacon_enabled ? */ if (vif->bss_conf.enable_beacon)
il->beacon_enabled = true; else
il->beacon_enabled = false;
}
if (changes & BSS_CHANGED_BSSID) {
D_MAC80211("BSSID %pM\n", bss_conf->bssid);
/* * On passive channel we wait with blocked queues to see if * there is traffic on that channel. If no frame will be * received (what is very unlikely since scan detects AP on * that channel, but theoretically possible), mac80211 associate * procedure will time out and mac80211 will call us with NULL * bssid. We have to unblock queues on such condition.
*/ if (is_zero_ether_addr(bss_conf->bssid))
il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
/* * If there is currently a HW scan going on in the background, * then we need to cancel it, otherwise sometimes we are not * able to authenticate (FIXME: why ?)
*/ if (il_scan_cancel_timeout(il, 100)) {
D_MAC80211("leave - scan abort failed\n");
mutex_unlock(&il->mutex); return;
}
/* mac80211 only sets assoc when in STATION mode */
memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
/* FIXME: currently needed in a few places */
memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
}
/* * This needs to be after setting the BSSID in case * mac80211 decides to do both changes at once because * it will invoke post_associate.
*/ if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON))
il_beacon_update(hw, vif);
if (changes & BSS_CHANGED_ERP_PREAMBLE) {
D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); if (bss_conf->use_short_preamble)
il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else
il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
}
if (changes & BSS_CHANGED_BASIC_RATES) { /* XXX use this information * * To do that, remove code from il_set_rate() and put something * like this here: * if (A-band) il->staging.ofdm_basic_rates = bss_conf->basic_rates; else il->staging.ofdm_basic_rates = bss_conf->basic_rates >> 4; il->staging.cck_basic_rates = bss_conf->basic_rates & 0xF;
*/
}
if (changes & BSS_CHANGED_HT) {
il_ht_conf(il, vif);
if (il->ops->set_rxon_chain)
il->ops->set_rxon_chain(il);
}
if (changes & BSS_CHANGED_ASSOC) {
D_MAC80211("ASSOC %d\n", vif->cfg.assoc); if (vif->cfg.assoc) {
il->timestamp = bss_conf->sync_tsf;
if (!il_is_rfkill(il))
il->ops->post_associate(il);
} else
il_set_no_assoc(il, vif);
}
if (changes && il_is_associated(il) && vif->cfg.aid) {
D_MAC80211("Changes (%#llx) while associated\n", changes);
ret = il_send_rxon_assoc(il); if (!ret) { /* Sync active_rxon with latest change. */
memcpy((void *)&il->active, &il->staging, sizeof(struct il_rxon_cmd));
}
}
if (changes & BSS_CHANGED_BEACON_ENABLED) { if (vif->bss_conf.enable_beacon) {
memcpy(il->staging.bssid_addr, bss_conf->bssid,
ETH_ALEN);
memcpy(il->bssid, bss_conf->bssid, ETH_ALEN);
il->ops->config_ap(il);
} else
il_set_no_assoc(il, vif);
}
if (changes & BSS_CHANGED_IBSS) {
ret = il->ops->manage_ibss_station(il, vif,
vif->cfg.ibss_joined); if (ret)
IL_ERR("failed to %s IBSS station %pM\n",
vif->cfg.ibss_joined ? "add" : "remove",
bss_conf->bssid);
}
/* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. * If we have something to service, the tasklet will re-enable ints.
* If we *don't* have something, we'll re-enable before leaving here. */
inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */
_il_wr(il, CSR_INT_MASK, 0x00000000);
/* Discover which interrupts are active/pending */
inta = _il_rd(il, CSR_INT);
inta_fh = _il_rd(il, CSR_FH_INT_STATUS);
/* Ignore interrupt if there's nothing in NIC to service. * This may be due to IRQ shared with another device,
* or due to sporadic interrupts thrown from our NIC. */ if (!inta && !inta_fh) {
D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); goto none;
}
if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) { /* Hardware disappeared. It might have already raised
* an interrupt */
IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta); goto unplugged;
}
none: /* re-enable interrupts here since we don't have anything to service. */ /* only Re-enable if disabled by irq */ if (test_bit(S_INT_ENABLED, &il->status))
il_enable_interrupts(il);
spin_unlock_irqrestore(&il->lock, flags); return IRQ_NONE;
}
EXPORT_SYMBOL(il_isr);
/* * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this * function.
*/ void
il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info,
__le16 fc, __le32 *tx_flags)
{ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
*tx_flags |= TX_CMD_FLG_RTS_MSK;
*tx_flags &= ~TX_CMD_FLG_CTS_MSK;
*tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
if (!ieee80211_is_mgmt(fc)) return;
switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { case cpu_to_le16(IEEE80211_STYPE_AUTH): case cpu_to_le16(IEEE80211_STYPE_DEAUTH): case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
*tx_flags &= ~TX_CMD_FLG_RTS_MSK;
*tx_flags |= TX_CMD_FLG_CTS_MSK; break;
}
} elseif (info->control.rates[0].
flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
*tx_flags &= ~TX_CMD_FLG_RTS_MSK;
*tx_flags |= TX_CMD_FLG_CTS_MSK;
*tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
}
}
EXPORT_SYMBOL(il_tx_cmd_protection);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.91 Sekunden
(vorverarbeitet am 2026-04-28)
¤
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.