static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, int dynps_ms, int wakeup_period) { /* * These are the original power level 3 sleep successions. The * device may behave better with such succession and was also * only tested with that. Just like the original sleep commands, * also adjust the succession here to the wakeup_period below. * The ranges are the same as for the sleep commands, 0-2, 3-9 * and >10, which is selected based on the DTIM interval for * the sleep index but here we use the wakeup period since that * is what we need to do for the latency requirements. */ static const u8 slp_succ_r0[IWL_POWER_VEC_SIZE] = { 2, 2, 2, 2, 2 }; static const u8 slp_succ_r1[IWL_POWER_VEC_SIZE] = { 2, 4, 6, 7, 9 }; static const u8 slp_succ_r2[IWL_POWER_VEC_SIZE] = { 2, 7, 9, 9, 0xFF }; const u8 *slp_succ = slp_succ_r0; int i; if (wakeup_period > IWL_DTIM_RANGE_0_MAX) slp_succ = slp_succ_r1; if (wakeup_period > IWL_DTIM_RANGE_1_MAX) slp_succ = slp_succ_r2; memset(cmd, 0, sizeof(*cmd)); cmd->flags = IWL_POWER_DRIVER_ALLOW_SLEEP_MSK | IWL_POWER_FAST_PD; /* no use seeing frames for others */ if (priv->power_data.pci_pm) cmd->flags |= IWL_POWER_PCI_PM_MSK; if (priv->cfg->base_params->shadow_reg_enable) cmd->flags |= IWL_POWER_SHADOW_REG_ENA; else cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { if (!priv->cfg->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; } cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms); cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms); for (i = 0; i < IWL_POWER_VEC_SIZE; i++) cmd->sleep_interval[i] = cpu_to_le32(min_t(int, slp_succ[i], wakeup_period)); IWL_DEBUG_POWER(priv, "Automatic sleep command\n"); }
/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ static int iwl_rx_scan_complete_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", scan_notif->scanned_channels, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", jiffies_to_msecs(jiffies - priv->scan_start)); /* * When aborting, we run the scan completed background work inline * and the background work must then do nothing. The SCAN_COMPLETE * bit helps implement that logic and thus needs to be set before * queueing the work. Also, since the scan abort waits for SCAN_HW * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW * to avoid a race there. */ set_bit(STATUS_SCAN_COMPLETE, &priv->status); clear_bit(STATUS_SCAN_HW, &priv->status); queue_work(priv->workqueue, &priv->scan_completed); if (priv->iw_mode != NL80211_IFTYPE_ADHOC && iwl_advanced_bt_coexist(priv) && priv->bt_status != scan_notif->bt_status) { if (scan_notif->bt_status) { /* BT on */ if (!priv->bt_ch_announce) priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; /* * otherwise, no traffic load information provided * no changes made */ } else { /* BT off */ priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE; } priv->bt_status = scan_notif->bt_status; queue_work(priv->workqueue, &priv->bt_traffic_change_work); } return 0; }
/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw; IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", scan_notif->scanned_channels, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); /* The HW is no longer scanning */ clear_bit(STATUS_SCAN_HW, &priv->status); IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", jiffies_to_msecs(jiffies - priv->scan_start)); queue_work(priv->workqueue, &priv->scan_completed); if (priv->iw_mode != NL80211_IFTYPE_ADHOC && iwl_advanced_bt_coexist(priv) && priv->bt_status != scan_notif->bt_status) { if (scan_notif->bt_status) { /* BT on */ if (!priv->bt_ch_announce) priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; /* * otherwise, no traffic load information provided * no changes made */ } else { /* BT off */ priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE; } priv->bt_status = scan_notif->bt_status; queue_work(priv->workqueue, &priv->bt_traffic_change_work); } }
static int iwl_rx_scan_complete_notif(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", scan_notif->scanned_channels, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", jiffies_to_msecs(jiffies - priv->scan_start)); set_bit(STATUS_SCAN_COMPLETE, &priv->status); clear_bit(STATUS_SCAN_HW, &priv->status); queue_work(priv->workqueue, &priv->scan_completed); if (priv->iw_mode != NL80211_IFTYPE_ADHOC && iwl_advanced_bt_coexist(priv) && priv->bt_status != scan_notif->bt_status) { if (scan_notif->bt_status) { if (!priv->bt_ch_announce) priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; } else { priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE; } priv->bt_status = scan_notif->bt_status; queue_work(priv->workqueue, &priv->bt_traffic_change_work); } return 0; }
static void iwl_static_sleep_cmd(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, enum iwl_power_level lvl, int period) { const struct iwl_power_vec_entry *table; int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; int i; u8 skip; u32 slp_itrvl; if (priv->cfg->adv_pm) { table = apm_range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = apm_range_1; if (period <= IWL_DTIM_RANGE_0_MAX) table = apm_range_0; } else { table = range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = range_1; if (period <= IWL_DTIM_RANGE_0_MAX) table = range_0; } if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) memset(cmd, 0, sizeof(*cmd)); else *cmd = table[lvl].cmd; if (period == 0) { skip = 0; period = 1; for (i = 0; i < IWL_POWER_VEC_SIZE; i++) max_sleep[i] = 1; } else { skip = table[lvl].no_dtim; for (i = 0; i < IWL_POWER_VEC_SIZE; i++) max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; } slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); /* figure out the listen interval based on dtim period and skip */ if (slp_itrvl == 0xFF) cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = cpu_to_le32(period * (skip + 1)); slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); if (slp_itrvl > period) cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = cpu_to_le32((slp_itrvl / period) * period); if (skip) cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; else cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; if (priv->cfg->base_params->shadow_reg_enable) cmd->flags |= IWL_POWER_SHADOW_REG_ENA; else cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { if (!priv->cfg->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; } slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); /* enforce max sleep interval */ for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { if (le32_to_cpu(cmd->sleep_interval[i]) > (max_sleep[i] * period)) cmd->sleep_interval[i] = cpu_to_le32(max_sleep[i] * period); if (i != (IWL_POWER_VEC_SIZE - 1)) { if (le32_to_cpu(cmd->sleep_interval[i]) > le32_to_cpu(cmd->sleep_interval[i+1])) cmd->sleep_interval[i] = cmd->sleep_interval[i+1]; } } if (priv->power_data.pci_pm) cmd->flags |= IWL_POWER_PCI_PM_MSK; else cmd->flags &= ~IWL_POWER_PCI_PM_MSK; IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", skip, period); IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); }