mwifiex: add SDIO rx single port aggregation
authorZhaoyang Liu <liuzy@marvell.com>
Fri, 13 Mar 2015 12:07:58 +0000 (17:37 +0530)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 16 Mar 2015 16:12:39 +0000 (18:12 +0200)
This patch brings in support for SDIO single port rx aggregation
to mwifiex.
Maximum read size support by SDIO cmd53 is 64K.
Based on multi port aggregation which is already part of mwifiex, idea here
is multiple packets received in FW can be aggregated into single buffer.
A separate upload type is defined for such packet aggregated to single port.
Packets from this single buffer are later deaggregated into individual packets.
This way, driver can receive more packets each time through single SDIO cmd53;
thereby reducing no of times MMC bus is accessed.

SDIO SP aggregation support is advertised by FW during load time and driver
would get FW block size in command response of HostCmd_CMD_SDIO_SP_RX_AGGR_CFG.

Signed-off-by: Zhaoyang Liu <liuzy@marvell.com>
Signed-off-by: Marc Yang <yangyang@marvell.com>
Reviewed-by: Amitkumar Karwar <akarwar@marvell.com>
Reviewed-by: Cathy Luo <cluo@marvell.com>
Reviewed-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c

index 6ce19bed0e91fa289947d9b4a3d85665438ecee3..38f24e0427d28b02a979eaec1ce066e648829048 100644 (file)
 
 #define MWIFIEX_A_BAND_START_FREQ      5000
 
+/* SDIO Aggr data packet special info */
+#define SDIO_MAX_AGGR_BUF_SIZE         (256 * 255)
+#define BLOCK_NUMBER_OFFSET            15
+#define SDIO_HEADER_OFFSET             28
+
 enum mwifiex_bss_type {
        MWIFIEX_BSS_TYPE_STA = 0,
        MWIFIEX_BSS_TYPE_UAP = 1,
@@ -169,10 +174,11 @@ struct mwifiex_wait_queue {
 };
 
 struct mwifiex_rxinfo {
+       struct sk_buff *parent;
        u8 bss_num;
        u8 bss_type;
-       struct sk_buff *parent;
        u8 use_count;
+       u8 buf_type;
 };
 
 struct mwifiex_txinfo {
index 21a942fd2226ad4c41dea0400cd8e53291ae782c..59d8964dd0dcaaadc39d0c09f872fe46c5488c4d 100644 (file)
@@ -197,6 +197,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 
 #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
 #define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
+#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16))
 
 #define MWIFIEX_DEF_HT_CAP     (IEEE80211_HT_CAP_DSSSCCK40 | \
                                 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
@@ -353,6 +354,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_REMAIN_ON_CHAN                    0x010d
 #define HostCmd_CMD_11AC_CFG                         0x0112
 #define HostCmd_CMD_TDLS_OPER                         0x0122
+#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG               0x0223
 
 #define PROTOCOL_NO_SECURITY        0x01
 #define PROTOCOL_STATIC_WEP         0x02
@@ -1242,6 +1244,12 @@ struct host_cmd_ds_chan_rpt_event {
        u8 tlvbuf[0];
 } __packed;
 
+struct host_cmd_sdio_sp_rx_aggr_cfg {
+       u8 action;
+       u8 enable;
+       __le16 block_size;
+} __packed;
+
 struct mwifiex_fixed_bcn_param {
        __le64 timestamp;
        __le16 beacon_period;
@@ -1964,6 +1972,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_coalesce_cfg coalesce_cfg;
                struct host_cmd_ds_tdls_oper tdls_oper;
                struct host_cmd_ds_chan_rpt_req chan_rpt_req;
+               struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
        } params;
 } __packed;
 
index b242c3e8df3fe3dbb8785ee76179f80a22b14be2..eaaacecbdef65a1b1e1e45e7b86126194d251990 100644 (file)
@@ -163,6 +163,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
 {
        unsigned long flags;
        struct sk_buff *skb;
+       struct mwifiex_rxinfo *rx_info;
 
        spin_lock_irqsave(&adapter->rx_proc_lock, flags);
        if (adapter->rx_processing || adapter->rx_locked) {
@@ -184,7 +185,14 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
                        adapter->delay_main_work = false;
                        mwifiex_queue_main_work(adapter);
                }
-               mwifiex_handle_rx_packet(adapter, skb);
+               rx_info = MWIFIEX_SKB_RXCB(skb);
+               if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
+                       if (adapter->if_ops.deaggr_pkt)
+                               adapter->if_ops.deaggr_pkt(adapter, skb);
+                       dev_kfree_skb_any(skb);
+               } else {
+                       mwifiex_handle_rx_packet(adapter, skb);
+               }
        }
        spin_lock_irqsave(&adapter->rx_proc_lock, flags);
        adapter->rx_processing = false;
index 11db09c73e85713e28d8e0f752e9c51ca6a92470..842fa0beb18841c4dbfee76f27a57b6b6b0c6072 100644 (file)
@@ -121,6 +121,7 @@ enum {
 
 #define MWIFIEX_TYPE_CMD                               1
 #define MWIFIEX_TYPE_DATA                              0
+#define MWIFIEX_TYPE_AGGR_DATA                         10
 #define MWIFIEX_TYPE_EVENT                             3
 
 #define MAX_BITMAP_RATES_SIZE                  18
@@ -744,6 +745,7 @@ struct mwifiex_if_ops {
        int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
        void (*iface_work)(struct work_struct *work);
        void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
+       void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *);
 };
 
 struct mwifiex_adapter {
@@ -787,6 +789,8 @@ struct mwifiex_adapter {
        u8 more_task_flag;
        u16 tx_buf_size;
        u16 curr_tx_buf_size;
+       bool sdio_rx_aggr_enable;
+       u16 sdio_rx_block_size;
        u32 ioport;
        enum MWIFIEX_HARDWARE_STATUS hw_status;
        u16 number_of_antenna;
index 509204f5ae6f3a97988bca98be50ea2ad463f28b..fdeeb67f790ac4c42f4bac793aef65d0075a9737 100644 (file)
@@ -1042,6 +1042,59 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
        return ret;
 }
 
+/*
+ * This function decode sdio aggreation pkt.
+ *
+ * Based on the the data block size and pkt_len,
+ * skb data will be decoded to few packets.
+ */
+static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter,
+                                   struct sk_buff *skb)
+{
+       u32 total_pkt_len, pkt_len;
+       struct sk_buff *skb_deaggr;
+       u32 pkt_type;
+       u16 blk_size;
+       u8 blk_num;
+       u8 *data;
+
+       data = skb->data;
+       total_pkt_len = skb->len;
+
+       while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) {
+               if (total_pkt_len < adapter->sdio_rx_block_size)
+                       break;
+               blk_num = *(data + BLOCK_NUMBER_OFFSET);
+               blk_size = adapter->sdio_rx_block_size * blk_num;
+               if (blk_size > total_pkt_len) {
+                       dev_err(adapter->dev, "%s: error in pkt,\t"
+                               "blk_num=%d, blk_size=%d, total_pkt_len=%d\n",
+                               __func__, blk_num, blk_size, total_pkt_len);
+                       break;
+               }
+               pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET));
+               pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET +
+                                        2));
+               if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) {
+                       dev_err(adapter->dev, "%s: error in pkt,\t"
+                               "pkt_len=%d, blk_size=%d\n",
+                               __func__, pkt_len, blk_size);
+                       break;
+               }
+               skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len,
+                                                        GFP_KERNEL | GFP_DMA);
+               if (!skb_deaggr)
+                       break;
+               skb_put(skb_deaggr, pkt_len);
+               memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len);
+               skb_pull(skb_deaggr, INTF_HEADER_LEN);
+
+               mwifiex_handle_rx_packet(adapter, skb_deaggr);
+               data += blk_size;
+               total_pkt_len -= blk_size;
+       }
+}
+
 /*
  * This function decodes a received packet.
  *
@@ -1055,11 +1108,28 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
        u8 *cmd_buf;
        __le16 *curr_ptr = (__le16 *)skb->data;
        u16 pkt_len = le16_to_cpu(*curr_ptr);
+       struct mwifiex_rxinfo *rx_info;
 
-       skb_trim(skb, pkt_len);
-       skb_pull(skb, INTF_HEADER_LEN);
+       if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) {
+               skb_trim(skb, pkt_len);
+               skb_pull(skb, INTF_HEADER_LEN);
+       }
 
        switch (upld_typ) {
+       case MWIFIEX_TYPE_AGGR_DATA:
+               dev_dbg(adapter->dev, "info: --- Rx: Aggr Data packet ---\n");
+               rx_info = MWIFIEX_SKB_RXCB(skb);
+               rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA;
+               if (adapter->rx_work_enabled) {
+                       skb_queue_tail(&adapter->rx_data_q, skb);
+                       atomic_inc(&adapter->rx_pending);
+                       adapter->data_received = true;
+               } else {
+                       mwifiex_deaggr_sdio_pkt(adapter, skb);
+                       dev_kfree_skb_any(skb);
+               }
+               break;
+
        case MWIFIEX_TYPE_DATA:
                dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n");
                if (adapter->rx_work_enabled) {
@@ -1247,8 +1317,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
                        /* copy pkt to deaggr buf */
                        skb_deaggr = card->mpa_rx.skb_arr[pind];
 
-                       if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <=
-                                        card->mpa_rx.len_arr[pind])) {
+                       if ((pkt_type == MWIFIEX_TYPE_DATA ||
+                            (pkt_type == MWIFIEX_TYPE_AGGR_DATA &&
+                             adapter->sdio_rx_aggr_enable)) &&
+                           (pkt_len <= card->mpa_rx.len_arr[pind])) {
 
                                memcpy(skb_deaggr->data, curr_ptr, pkt_len);
 
@@ -1258,8 +1330,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
                                mwifiex_decode_rx_packet(adapter, skb_deaggr,
                                                         pkt_type);
                        } else {
-                               dev_err(adapter->dev, "wrong aggr pkt:"
-                                       " type=%d len=%d max_len=%d\n",
+                               dev_err(adapter->dev, "wrong aggr pkt:\t"
+                                       "sdio_single_port_rx_aggr=%d\t"
+                                       "type=%d len=%d max_len=%d\n",
+                                       adapter->sdio_rx_aggr_enable,
                                        pkt_type, pkt_len,
                                        card->mpa_rx.len_arr[pind]);
                                dev_kfree_skb_any(skb_deaggr);
@@ -1278,6 +1352,13 @@ rx_curr_single:
                                              skb->data, skb->len,
                                              adapter->ioport + port))
                        goto error;
+               if (!adapter->sdio_rx_aggr_enable &&
+                   pkt_type == MWIFIEX_TYPE_AGGR_DATA) {
+                       dev_err(adapter->dev, "Wrong pkt type %d\t"
+                               "Current SDIO RX Aggr not enabled\n",
+                               pkt_type);
+                       goto error;
+               }
 
                mwifiex_decode_rx_packet(adapter, skb, pkt_type);
        }
@@ -1452,7 +1533,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
                                 1) / MWIFIEX_SDIO_BLOCK_SIZE;
                        if (rx_len <= INTF_HEADER_LEN ||
                            (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
-                            MWIFIEX_RX_DATA_BUF_SIZE) {
+                            card->mpa_rx.buf_size) {
                                dev_err(adapter->dev, "invalid rx_len=%d\n",
                                        rx_len);
                                return -1;
@@ -1742,6 +1823,7 @@ static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter,
                                   u32 mpa_tx_buf_size, u32 mpa_rx_buf_size)
 {
        struct sdio_mmc_card *card = adapter->card;
+       u32 rx_buf_size;
        int ret = 0;
 
        card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL);
@@ -1752,13 +1834,15 @@ static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter,
 
        card->mpa_tx.buf_size = mpa_tx_buf_size;
 
-       card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL);
+       rx_buf_size = max_t(u32, mpa_rx_buf_size,
+                           (u32)SDIO_MAX_AGGR_BUF_SIZE);
+       card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL);
        if (!card->mpa_rx.buf) {
                ret = -1;
                goto error;
        }
 
-       card->mpa_rx.buf_size = mpa_rx_buf_size;
+       card->mpa_rx.buf_size = rx_buf_size;
 
 error:
        if (ret) {
@@ -2298,6 +2382,7 @@ static struct mwifiex_if_ops sdio_ops = {
        .iface_work = mwifiex_sdio_work,
        .fw_dump = mwifiex_sdio_fw_dump,
        .reg_dump = mwifiex_sdio_reg_dump,
+       .deaggr_pkt = mwifiex_deaggr_sdio_pkt,
 };
 
 /*
index b23eaed847016f92fbcab6fa4bd7e1caeff4b7c0..49422f2a53809fe0c241de93afb231c8011871c3 100644 (file)
@@ -1671,6 +1671,25 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
 
        return 0;
 }
+
+/* This function prepares command of sdio rx aggr info. */
+static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd,
+                                       u16 cmd_action, void *data_buf)
+{
+       struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+                                       &cmd->params.sdio_rx_aggr_cfg;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG);
+       cmd->size =
+               cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) +
+                           S_DS_GEN);
+       cfg->action = cmd_action;
+       if (cmd_action == HostCmd_ACT_GEN_SET)
+               cfg->enable = *(u8 *)data_buf;
+
+       return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1908,6 +1927,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
                ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr,
                                                            data_buf);
                break;
+       case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+               ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action,
+                                                  data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);
@@ -1947,6 +1970,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
        struct mwifiex_ds_auto_ds auto_ds;
        enum state_11d_t state_11d;
        struct mwifiex_ds_11n_tx_cfg tx_cfg;
+       u8 sdio_sp_rx_aggr_enable;
 
        if (first_sta) {
                if (priv->adapter->iface_type == MWIFIEX_PCIE) {
@@ -1990,6 +2014,22 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
                if (ret)
                        return -1;
 
+               /** Set SDIO Single Port RX Aggr Info */
+               if (priv->adapter->iface_type == MWIFIEX_SDIO &&
+                   ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) {
+                       sdio_sp_rx_aggr_enable = true;
+                       ret = mwifiex_send_cmd(priv,
+                                              HostCmd_CMD_SDIO_SP_RX_AGGR_CFG,
+                                              HostCmd_ACT_GEN_SET, 0,
+                                              &sdio_sp_rx_aggr_enable,
+                                              true);
+                       if (ret) {
+                               dev_err(priv->adapter->dev,
+                                       "error while enabling SP aggregation..disable it");
+                               adapter->sdio_rx_aggr_enable = false;
+                       }
+               }
+
                /* Reconfigure tx buf size */
                ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
                                       HostCmd_ACT_GEN_SET, 0,
index 5f8da5924666275615482fd07de75fa46d4bfb4a..88dc6b672ef43adb5cc8c1b836b19a1bed0db5d1 100644 (file)
@@ -90,6 +90,10 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
        case HostCmd_CMD_MAC_CONTROL:
                break;
 
+       case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+               dev_err(priv->adapter->dev, "SDIO RX single-port aggregation Not support\n");
+               break;
+
        default:
                break;
        }
@@ -943,6 +947,20 @@ static int mwifiex_ret_cfg_data(struct mwifiex_private *priv,
        return 0;
 }
 
+/** This Function handles the command response of sdio rx aggr */
+static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv,
+                                       struct host_cmd_ds_command *resp)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+                               &resp->params.sdio_rx_aggr_cfg;
+
+       adapter->sdio_rx_aggr_enable = cfg->enable;
+       adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size);
+
+       return 0;
+}
+
 /*
  * This function handles the command responses.
  *
@@ -1124,6 +1142,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                break;
        case HostCmd_CMD_CHAN_REPORT_REQUEST:
                break;
+       case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+               ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp);
+               break;
        default:
                dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
                        resp->command);