|
@@ -135,6 +135,14 @@ struct mwl8k_tx_queue {
|
|
|
struct sk_buff **skb;
|
|
|
};
|
|
|
|
|
|
+struct mwl8k_ampdu_stream {
|
|
|
+ struct ieee80211_sta *sta;
|
|
|
+ u8 tid;
|
|
|
+ u8 state;
|
|
|
+ u8 idx;
|
|
|
+ u8 txq_idx; /* index of this stream in priv->txq */
|
|
|
+};
|
|
|
+
|
|
|
struct mwl8k_priv {
|
|
|
struct ieee80211_hw *hw;
|
|
|
struct pci_dev *pdev;
|
|
@@ -360,6 +368,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {
|
|
|
#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */
|
|
|
#define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */
|
|
|
#define MWL8K_CMD_UPDATE_STADB 0x1123
|
|
|
+#define MWL8K_CMD_BASTREAM 0x1125
|
|
|
|
|
|
static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
|
|
|
{
|
|
@@ -399,6 +408,7 @@ static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
|
|
|
MWL8K_CMDNAME(SET_NEW_STN);
|
|
|
MWL8K_CMDNAME(UPDATE_ENCRYPTION);
|
|
|
MWL8K_CMDNAME(UPDATE_STADB);
|
|
|
+ MWL8K_CMDNAME(BASTREAM);
|
|
|
default:
|
|
|
snprintf(buf, bufsize, "0x%x", cmd);
|
|
|
}
|
|
@@ -3175,6 +3185,152 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * CMD_BASTREAM.
|
|
|
+ */
|
|
|
+
|
|
|
+/*
|
|
|
+ * UPSTREAM is tx direction
|
|
|
+ */
|
|
|
+#define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00
|
|
|
+#define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01
|
|
|
+
|
|
|
+enum {
|
|
|
+ MWL8K_BA_CREATE,
|
|
|
+ MWL8K_BA_UPDATE,
|
|
|
+ MWL8K_BA_DESTROY,
|
|
|
+ MWL8K_BA_FLUSH,
|
|
|
+ MWL8K_BA_CHECK,
|
|
|
+} ba_stream_action_type;
|
|
|
+
|
|
|
+
|
|
|
+struct mwl8k_create_ba_stream {
|
|
|
+ __le32 flags;
|
|
|
+ __le32 idle_thrs;
|
|
|
+ __le32 bar_thrs;
|
|
|
+ __le32 window_size;
|
|
|
+ u8 peer_mac_addr[6];
|
|
|
+ u8 dialog_token;
|
|
|
+ u8 tid;
|
|
|
+ u8 queue_id;
|
|
|
+ u8 param_info;
|
|
|
+ __le32 ba_context;
|
|
|
+ u8 reset_seq_no_flag;
|
|
|
+ __le16 curr_seq_no;
|
|
|
+ u8 sta_src_mac_addr[6];
|
|
|
+} __packed;
|
|
|
+
|
|
|
+struct mwl8k_destroy_ba_stream {
|
|
|
+ __le32 flags;
|
|
|
+ __le32 ba_context;
|
|
|
+} __packed;
|
|
|
+
|
|
|
+struct mwl8k_cmd_bastream {
|
|
|
+ struct mwl8k_cmd_pkt header;
|
|
|
+ __le32 action;
|
|
|
+ union {
|
|
|
+ struct mwl8k_create_ba_stream create_params;
|
|
|
+ struct mwl8k_destroy_ba_stream destroy_params;
|
|
|
+ };
|
|
|
+} __packed;
|
|
|
+
|
|
|
+static int
|
|
|
+mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
|
|
|
+{
|
|
|
+ struct mwl8k_cmd_bastream *cmd;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
|
|
+ if (cmd == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
|
|
|
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
|
|
|
+
|
|
|
+ cmd->action = cpu_to_le32(MWL8K_BA_CHECK);
|
|
|
+
|
|
|
+ cmd->create_params.queue_id = stream->idx;
|
|
|
+ memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr,
|
|
|
+ ETH_ALEN);
|
|
|
+ cmd->create_params.tid = stream->tid;
|
|
|
+
|
|
|
+ cmd->create_params.flags =
|
|
|
+ cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) |
|
|
|
+ cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM);
|
|
|
+
|
|
|
+ rc = mwl8k_post_cmd(hw, &cmd->header);
|
|
|
+
|
|
|
+ kfree(cmd);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
|
|
|
+ u8 buf_size)
|
|
|
+{
|
|
|
+ struct mwl8k_cmd_bastream *cmd;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
|
|
+ if (cmd == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+
|
|
|
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
|
|
|
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
|
|
|
+
|
|
|
+ cmd->action = cpu_to_le32(MWL8K_BA_CREATE);
|
|
|
+
|
|
|
+ cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size);
|
|
|
+ cmd->create_params.window_size = cpu_to_le32((u32)buf_size);
|
|
|
+ cmd->create_params.queue_id = stream->idx;
|
|
|
+
|
|
|
+ memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN);
|
|
|
+ cmd->create_params.tid = stream->tid;
|
|
|
+ cmd->create_params.curr_seq_no = cpu_to_le16(0);
|
|
|
+ cmd->create_params.reset_seq_no_flag = 1;
|
|
|
+
|
|
|
+ cmd->create_params.param_info =
|
|
|
+ (stream->sta->ht_cap.ampdu_factor &
|
|
|
+ IEEE80211_HT_AMPDU_PARM_FACTOR) |
|
|
|
+ ((stream->sta->ht_cap.ampdu_density << 2) &
|
|
|
+ IEEE80211_HT_AMPDU_PARM_DENSITY);
|
|
|
+
|
|
|
+ cmd->create_params.flags =
|
|
|
+ cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE |
|
|
|
+ BASTREAM_FLAG_DIRECTION_UPSTREAM);
|
|
|
+
|
|
|
+ rc = mwl8k_post_cmd(hw, &cmd->header);
|
|
|
+
|
|
|
+ wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n",
|
|
|
+ stream->sta->addr, stream->tid);
|
|
|
+ kfree(cmd);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static void mwl8k_destroy_ba(struct ieee80211_hw *hw,
|
|
|
+ struct mwl8k_ampdu_stream *stream)
|
|
|
+{
|
|
|
+ struct mwl8k_cmd_bastream *cmd;
|
|
|
+
|
|
|
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
|
|
+ if (cmd == NULL)
|
|
|
+ return;
|
|
|
+
|
|
|
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
|
|
|
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
|
|
|
+ cmd->action = cpu_to_le32(MWL8K_BA_DESTROY);
|
|
|
+
|
|
|
+ cmd->destroy_params.ba_context = cpu_to_le32(stream->idx);
|
|
|
+ mwl8k_post_cmd(hw, &cmd->header);
|
|
|
+
|
|
|
+ wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", stream->idx);
|
|
|
+
|
|
|
+ kfree(cmd);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* CMD_SET_NEW_STN.
|
|
|
*/
|