Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ab10ff8 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

iwlwifi: handle unicast PS buffering



Using the new mac80211 functionality, this makes
iwlwifi handle unicast PS buffering correctly.
The device works like this:

 * when a station goes to sleep, the microcode notices
   this and marks the station as asleep
 * when the station is marked asleep, the microcode
   refuses to transmit to the station and rejects all
   frames queued to it with the failure status code
   TX_STATUS_FAIL_DEST_PS (a previous patch handled
   this correctly)
 * when we need to send frames to the station _although_
   it is asleep, we need to tell the ucode how many,
   and this is asynchronous with sending so we cannot
   just send the frames, we need to wait for all other
   frames to be flushed, and then update the counter
   before sending out the poll response frames. This
   is handled partially in the driver and partially in
   mac80211.

In order to do all this correctly, we need to
 * keep track of how many frames are pending for each
   associated client station (avoid doing it for other
   stations to avoid the atomic ops)
 * tell mac80211 that we driver-block the PS status
   while there are still frames pending on the queues,
   and once they are all rejected (due to the dest sta
   being in PS) unblock mac80211

Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 9bb487b4
Loading
Loading
Loading
Loading
+41 −1
Original line number Original line Diff line number Diff line
@@ -2744,6 +2744,45 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw,
	return 0;
	return 0;
}
}


static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
			       struct ieee80211_vif *vif,
			       enum sta_notify_cmd cmd,
			       struct ieee80211_sta *sta)
{
	struct iwl_priv *priv = hw->priv;
	struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
	int sta_id;

	/*
	 * TODO: We really should use this callback to
	 *	 actually maintain the station table in
	 *	 the device.
	 */

	switch (cmd) {
	case STA_NOTIFY_ADD:
		atomic_set(&sta_priv->pending_frames, 0);
		if (vif->type == NL80211_IFTYPE_AP)
			sta_priv->client = true;
		break;
	case STA_NOTIFY_SLEEP:
		WARN_ON(!sta_priv->client);
		sta_priv->asleep = true;
		if (atomic_read(&sta_priv->pending_frames) > 0)
			ieee80211_sta_block_awake(hw, sta, true);
		break;
	case STA_NOTIFY_AWAKE:
		WARN_ON(!sta_priv->client);
		sta_priv->asleep = false;
		sta_id = iwl_find_station(priv, sta->addr);
		if (sta_id != IWL_INVALID_STATION)
			iwl_sta_modify_ps_wake(priv, sta_id);
		break;
	default:
		break;
	}
}

/*****************************************************************************
/*****************************************************************************
 *
 *
 * sysfs attributes
 * sysfs attributes
@@ -3175,7 +3214,8 @@ static struct ieee80211_ops iwl_hw_ops = {
	.reset_tsf = iwl_mac_reset_tsf,
	.reset_tsf = iwl_mac_reset_tsf,
	.bss_info_changed = iwl_bss_info_changed,
	.bss_info_changed = iwl_bss_info_changed,
	.ampdu_action = iwl_mac_ampdu_action,
	.ampdu_action = iwl_mac_ampdu_action,
	.hw_scan = iwl_mac_hw_scan
	.hw_scan = iwl_mac_hw_scan,
	.sta_notify = iwl_mac_sta_notify,
};
};


static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+1 −0
Original line number Original line Diff line number Diff line
@@ -977,6 +977,7 @@ struct iwl_qosparam_cmd {
#define	STA_MODIFY_TX_RATE_MSK		0x04
#define	STA_MODIFY_TX_RATE_MSK		0x04
#define STA_MODIFY_ADDBA_TID_MSK	0x08
#define STA_MODIFY_ADDBA_TID_MSK	0x08
#define STA_MODIFY_DELBA_TID_MSK	0x10
#define STA_MODIFY_DELBA_TID_MSK	0x10
#define STA_MODIFY_SLEEP_TX_COUNT_MSK	0x20


/* Receiver address (actually, Rx station's index into station table),
/* Receiver address (actually, Rx station's index into station table),
 * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
 * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
+0 −2
Original line number Original line Diff line number Diff line
@@ -336,8 +336,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
			pos += scnprintf(buf + pos, bufsz - pos,
			pos += scnprintf(buf + pos, bufsz - pos,
					"flags: 0x%x\n",
					"flags: 0x%x\n",
					station->sta.station_flags_msk);
					station->sta.station_flags_msk);
			pos += scnprintf(buf + pos, bufsz - pos,
					"ps_status: %u\n", station->ps_status);
			pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n");
			pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n");
			pos += scnprintf(buf + pos, bufsz - pos,
			pos += scnprintf(buf + pos, bufsz - pos,
					"seq_num\t\ttxq_id");
					"seq_num\t\ttxq_id");
+3 −7
Original line number Original line Diff line number Diff line
@@ -545,15 +545,11 @@ struct iwl_qos_info {
	struct iwl_qosparam_cmd def_qos_parm;
	struct iwl_qosparam_cmd def_qos_parm;
};
};


#define STA_PS_STATUS_WAKE             0
#define STA_PS_STATUS_SLEEP            1



struct iwl3945_station_entry {
struct iwl3945_station_entry {
	struct iwl3945_addsta_cmd sta;
	struct iwl3945_addsta_cmd sta;
	struct iwl_tid_data tid[MAX_TID_COUNT];
	struct iwl_tid_data tid[MAX_TID_COUNT];
	u8 used;
	u8 used;
	u8 ps_status;
	struct iwl_hw_key keyinfo;
	struct iwl_hw_key keyinfo;
};
};


@@ -561,7 +557,6 @@ struct iwl_station_entry {
	struct iwl_addsta_cmd sta;
	struct iwl_addsta_cmd sta;
	struct iwl_tid_data tid[MAX_TID_COUNT];
	struct iwl_tid_data tid[MAX_TID_COUNT];
	u8 used;
	u8 used;
	u8 ps_status;
	struct iwl_hw_key keyinfo;
	struct iwl_hw_key keyinfo;
};
};


@@ -571,11 +566,12 @@ struct iwl_station_entry {
 * When mac80211 creates a station it reserves some space (hw->sta_data_size)
 * When mac80211 creates a station it reserves some space (hw->sta_data_size)
 * in the structure for use by driver. This structure is places in that
 * in the structure for use by driver. This structure is places in that
 * space.
 * space.
 *
 * At the moment use it for the station's rate scaling information.
 */
 */
struct iwl_station_priv {
struct iwl_station_priv {
	struct iwl_lq_sta lq_sta;
	struct iwl_lq_sta lq_sta;
	atomic_t pending_frames;
	bool client;
	bool asleep;
};
};


/* one for each uCode image (inst/data, boot/init/runtime) */
/* one for each uCode image (inst/data, boot/init/runtime) */
+2 −15
Original line number Original line Diff line number Diff line
@@ -1028,7 +1028,6 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
	struct iwl4965_rx_mpdu_res_start *amsdu;
	struct iwl4965_rx_mpdu_res_start *amsdu;
	u32 len;
	u32 len;
	u32 ampdu_status;
	u32 ampdu_status;
	u16 fc;
	u32 rate_n_flags;
	u32 rate_n_flags;


	/**
	/**
@@ -1161,20 +1160,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
		priv->last_tsf = le64_to_cpu(phy_res->timestamp);
		priv->last_tsf = le64_to_cpu(phy_res->timestamp);
	}
	}


	fc = le16_to_cpu(header->frame_control);
	switch (fc & IEEE80211_FCTL_FTYPE) {
	case IEEE80211_FTYPE_MGMT:
	case IEEE80211_FTYPE_DATA:
		if (priv->iw_mode == NL80211_IFTYPE_AP)
			iwl_update_ps_mode(priv, fc  & IEEE80211_FCTL_PM,
						header->addr2);
		/* fall through */
	default:
	iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
	iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
				    rxb, &rx_status);
				    rxb, &rx_status);
		break;

	}
}
}
EXPORT_SYMBOL(iwl_rx_reply_rx);
EXPORT_SYMBOL(iwl_rx_reply_rx);


Loading