diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 6ce19bed0e91fa289947d9b4a3d85665438ecee3..38f24e0427d28b02a979eaec1ce066e648829048 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -112,6 +112,11 @@ #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 { diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 21a942fd2226ad4c41dea0400cd8e53291ae782c..59d8964dd0dcaaadc39d0c09f872fe46c5488c4d 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -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; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index b242c3e8df3fe3dbb8785ee76179f80a22b14be2..eaaacecbdef65a1b1e1e45e7b86126194d251990 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -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; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 11db09c73e85713e28d8e0f752e9c51ca6a92470..842fa0beb18841c4dbfee76f27a57b6b6b0c6072 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -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; diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 509204f5ae6f3a97988bca98be50ea2ad463f28b..fdeeb67f790ac4c42f4bac793aef65d0075a9737 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -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 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, 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, }; /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index b23eaed847016f92fbcab6fa4bd7e1caeff4b7c0..49422f2a53809fe0c241de93afb231c8011871c3 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -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, diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 5f8da5924666275615482fd07de75fa46d4bfb4a..88dc6b672ef43adb5cc8c1b836b19a1bed0db5d1 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -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);