mwifiex: usb: transmit aggregation packets
authorXinming Hu <huxm@marvell.com>
Tue, 23 May 2017 07:12:32 +0000 (07:12 +0000)
committerKalle Valo <kvalo@codeaurora.org>
Wed, 31 May 2017 13:54:08 +0000 (16:54 +0300)
Instead of using 4KB packet buffer for data transfer, new chipset have
more device memory. This patch try to aggregation packets in an 16KB
buffer. In this way, totally usb transaction cost will be reduced.

Thoughput test on usb 2.0 show both TCP TX and UPD TX promote ~40M,
from ~240M to ~280M.

This feature is default disabled, and can be enabled by module
parameter, like:
insmod mwifiex.ko aggr_ctrl=1

Signed-off-by: Xinming Hu <huxm@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/marvell/mwifiex/fw.h
drivers/net/wireless/marvell/mwifiex/main.c
drivers/net/wireless/marvell/mwifiex/main.h
drivers/net/wireless/marvell/mwifiex/sta_cmd.c
drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
drivers/net/wireless/marvell/mwifiex/usb.c
drivers/net/wireless/marvell/mwifiex/usb.h

index 6cf9ab9133ea37145c7ccb59d527b1e41b5cf2ea..b4d915b9232c9c207b64958d0c684636adbd4e0d 100644 (file)
@@ -405,6 +405,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_TDLS_OPER                         0x0122
 #define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG               0x0223
 #define HostCmd_CMD_CHAN_REGION_CFG                  0x0242
+#define HostCmd_CMD_PACKET_AGGR_CTRL                 0x0251
 
 #define PROTOCOL_NO_SECURITY        0x01
 #define PROTOCOL_STATIC_WEP         0x02
@@ -2268,6 +2269,14 @@ struct host_cmd_ds_chan_region_cfg {
        __le16 action;
 } __packed;
 
+struct host_cmd_ds_pkt_aggr_ctrl {
+       __le16 action;
+       __le16 enable;
+       __le16 tx_aggr_max_size;
+       __le16 tx_aggr_max_num;
+       __le16 tx_aggr_align;
+} __packed;
+
 struct host_cmd_ds_command {
        __le16 command;
        __le16 size;
@@ -2343,6 +2352,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_wakeup_reason hs_wakeup_reason;
                struct host_cmd_ds_gtk_rekey_params rekey;
                struct host_cmd_ds_chan_region_cfg reg_cfg;
+               struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl;
        } params;
 } __packed;
 
index dd87b9ff64c371911a74308be55e93e3a46cdd43..2c42191293c3f70d711a62b429896f670dba49b3 100644 (file)
@@ -44,6 +44,10 @@ bool mfg_mode;
 module_param(mfg_mode, bool, 0);
 MODULE_PARM_DESC(mfg_mode, "manufacturing mode enable:1, disable:0");
 
+bool aggr_ctrl;
+module_param(aggr_ctrl, bool, 0000);
+MODULE_PARM_DESC(aggr_ctrl, "usb tx aggreataon enable:1, disable:0");
+
 /*
  * This function registers the device and performs all the necessary
  * initializations.
index a4a014366d79aa647825231feafa7e707042d240..c37fb260650260387554108fd90607c0f083ffee 100644 (file)
@@ -60,6 +60,7 @@
 
 extern const char driver_version[];
 extern bool mfg_mode;
+extern bool aggr_ctrl;
 
 struct mwifiex_adapter;
 struct mwifiex_private;
@@ -798,6 +799,18 @@ struct mwifiex_auto_tdls_peer {
        u8 do_setup;
 };
 
+#define MWIFIEX_TYPE_AGGR_DATA_V2 11
+#define MWIFIEX_BUS_AGGR_MODE_LEN_V2 (2)
+#define MWIFIEX_BUS_AGGR_MAX_LEN 16000
+#define MWIFIEX_BUS_AGGR_MAX_NUM 10
+struct bus_aggr_params {
+       u16 enable;
+       u16 mode;
+       u16 tx_aggr_max_size;
+       u16 tx_aggr_max_num;
+       u16 tx_aggr_align;
+};
+
 struct mwifiex_if_ops {
        int (*init_if) (struct mwifiex_adapter *);
        void (*cleanup_if) (struct mwifiex_adapter *);
@@ -1016,6 +1029,8 @@ struct mwifiex_adapter {
        /* Wake-on-WLAN (WoWLAN) */
        int irq_wakeup;
        bool wake_by_wifi;
+       /* Aggregation parameters*/
+       struct bus_aggr_params bus_aggr;
 };
 
 void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
index 83916c1439af767ca4d03c2fab046170e44492d0..534d94a206a5d23dc0a1edceda7788041b892b9d 100644 (file)
@@ -2064,6 +2064,15 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
        case HostCmd_CMD_11AC_CFG:
                ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf);
                break;
+       case HostCmd_CMD_PACKET_AGGR_CTRL:
+               cmd_ptr->command = cpu_to_le16(cmd_no);
+               cmd_ptr->params.pkt_aggr_ctrl.action = cpu_to_le16(cmd_action);
+               cmd_ptr->params.pkt_aggr_ctrl.enable =
+                                               cpu_to_le16(*(u16 *)data_buf);
+               cmd_ptr->size =
+                       cpu_to_le16(sizeof(struct host_cmd_ds_pkt_aggr_ctrl) +
+                                   S_DS_GEN);
+               break;
        case HostCmd_CMD_P2P_MODE_CFG:
                cmd_ptr->command = cpu_to_le16(cmd_no);
                cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action);
@@ -2241,6 +2250,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
        enum state_11d_t state_11d;
        struct mwifiex_ds_11n_tx_cfg tx_cfg;
        u8 sdio_sp_rx_aggr_enable;
+       u16 packet_aggr_enable;
        int data;
 
        if (first_sta) {
@@ -2387,6 +2397,14 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
                                    "11D: failed to enable 11D\n");
        }
 
+       /* Pacekt aggregation handshake with firmware */
+       if (aggr_ctrl) {
+               packet_aggr_enable = true;
+               mwifiex_send_cmd(priv, HostCmd_CMD_PACKET_AGGR_CTRL,
+                                HostCmd_ACT_GEN_SET, 0,
+                                &packet_aggr_enable, true);
+       }
+
        /* Send cmd to FW to configure 11n specific configuration
         * (Short GI, Channel BW, Green field support etc.) for transmit
         */
index f1d1f56fc23ff739c3766e38bc723337a581ccb6..3348fb3a7514c31c585484089aa2eaf167fadff3 100644 (file)
@@ -1154,6 +1154,27 @@ static int mwifiex_ret_chan_region_cfg(struct mwifiex_private *priv,
        return 0;
 }
 
+int mwifiex_ret_pkt_aggr_ctrl(struct mwifiex_private *priv,
+                             struct host_cmd_ds_command *resp)
+{
+       struct host_cmd_ds_pkt_aggr_ctrl *pkt_aggr_ctrl =
+                                       &resp->params.pkt_aggr_ctrl;
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       adapter->bus_aggr.enable = le16_to_cpu(pkt_aggr_ctrl->enable);
+       if (adapter->bus_aggr.enable)
+               adapter->intf_hdr_len = INTF_HEADER_LEN;
+       adapter->bus_aggr.mode = MWIFIEX_BUS_AGGR_MODE_LEN_V2;
+       adapter->bus_aggr.tx_aggr_max_size =
+                               le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_size);
+       adapter->bus_aggr.tx_aggr_max_num =
+                               le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_num);
+       adapter->bus_aggr.tx_aggr_align =
+                               le16_to_cpu(pkt_aggr_ctrl->tx_aggr_align);
+
+       return 0;
+}
+
 /*
  * This function handles the command responses.
  *
@@ -1255,6 +1276,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                break;
        case HostCmd_CMD_11AC_CFG:
                break;
+       case HostCmd_CMD_PACKET_AGGR_CTRL:
+               ret = mwifiex_ret_pkt_aggr_ctrl(priv, resp);
+               break;
        case HostCmd_CMD_P2P_MODE_CFG:
                ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf);
                break;
index 9c3d654ae009e4baa183c9133ee8d52fc0610ea1..0ab455b03cdc706353c7ad29a32a0157e1b33bbc 100644 (file)
@@ -681,6 +681,7 @@ static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter)
                if (!port->tx_data_ep)
                        continue;
                port->tx_data_ix = 0;
+               skb_queue_head_init(&port->tx_aggr.aggr_list);
                if (port->tx_data_ep == MWIFIEX_USB_EP_DATA)
                        port->block_status = false;
                else
@@ -847,73 +848,31 @@ static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter)
        return true;
 }
 
-/* This function write a command/data packet to card. */
-static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
-                                   struct sk_buff *skb,
-                                   struct mwifiex_tx_param *tx_param)
+static int mwifiex_usb_construct_send_urb(struct mwifiex_adapter *adapter,
+                                         struct usb_tx_data_port *port, u8 ep,
+                                         struct urb_context *context,
+                                         struct sk_buff *skb_send)
 {
        struct usb_card_rec *card = adapter->card;
-       struct urb_context *context = NULL;
-       struct usb_tx_data_port *port = NULL;
-       u8 *data = (u8 *)skb->data;
+       int ret = -EINPROGRESS;
        struct urb *tx_urb;
-       int idx, ret = -EINPROGRESS;
-
-       if (adapter->is_suspended) {
-               mwifiex_dbg(adapter, ERROR,
-                           "%s: not allowed while suspended\n", __func__);
-               return -1;
-       }
-
-       if (adapter->surprise_removed) {
-               mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__);
-               return -1;
-       }
-
-       mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep);
-
-       if (ep == card->tx_cmd_ep) {
-               context = &card->tx_cmd;
-       } else {
-               for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) {
-                       if (ep == card->port[idx].tx_data_ep) {
-                               port = &card->port[idx];
-                               if (atomic_read(&port->tx_data_urb_pending)
-                                   >= MWIFIEX_TX_DATA_URB) {
-                                       port->block_status = true;
-                                       adapter->data_sent =
-                                               mwifiex_usb_data_sent(adapter);
-                                       return -EBUSY;
-                               }
-                               if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB)
-                                       port->tx_data_ix = 0;
-                               context =
-                                       &port->tx_data_list[port->tx_data_ix++];
-                               break;
-                       }
-               }
-               if (!port) {
-                       mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n");
-                       return -1;
-               }
-       }
 
        context->adapter = adapter;
        context->ep = ep;
-       context->skb = skb;
+       context->skb = skb_send;
        tx_urb = context->urb;
 
        if (ep == card->tx_cmd_ep &&
            card->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT)
                usb_fill_int_urb(tx_urb, card->udev,
-                                usb_sndintpipe(card->udev, ep), data,
-                                skb->len, mwifiex_usb_tx_complete,
+                                usb_sndintpipe(card->udev, ep), skb_send->data,
+                                skb_send->len, mwifiex_usb_tx_complete,
                                 (void *)context, card->tx_cmd_interval);
        else
                usb_fill_bulk_urb(tx_urb, card->udev,
-                                 usb_sndbulkpipe(card->udev, ep), data,
-                                 skb->len, mwifiex_usb_tx_complete,
-                                 (void *)context);
+                                 usb_sndbulkpipe(card->udev, ep),
+                                 skb_send->data, skb_send->len,
+                                 mwifiex_usb_tx_complete, (void *)context);
 
        tx_urb->transfer_flags |= URB_ZERO_PACKET;
 
@@ -950,6 +909,275 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
        return ret;
 }
 
+static int mwifiex_usb_prepare_tx_aggr_skb(struct mwifiex_adapter *adapter,
+                                          struct usb_tx_data_port *port,
+                                          struct sk_buff **skb_send)
+{
+       struct sk_buff *skb_aggr, *skb_tmp;
+       u8 *payload, pad;
+       u16 align = adapter->bus_aggr.tx_aggr_align;
+       struct mwifiex_txinfo *tx_info = NULL;
+       bool is_txinfo_set = false;
+
+       skb_aggr = mwifiex_alloc_dma_align_buf(port->tx_aggr.aggr_len,
+                                              GFP_ATOMIC);
+       if (!skb_aggr) {
+               mwifiex_dbg(adapter, ERROR,
+                           "%s: alloc skb_aggr failed\n", __func__);
+
+               while ((skb_tmp = skb_dequeue(&port->tx_aggr.aggr_list)))
+                       mwifiex_write_data_complete(adapter, skb_tmp, 0, -1);
+
+               port->tx_aggr.aggr_num = 0;
+               port->tx_aggr.aggr_len = 0;
+               return -EBUSY;
+       }
+
+       tx_info = MWIFIEX_SKB_TXCB(skb_aggr);
+       memset(tx_info, 0, sizeof(*tx_info));
+
+       while ((skb_tmp = skb_dequeue(&port->tx_aggr.aggr_list))) {
+               /* padding for aligning next packet header*/
+               pad = (align - (skb_tmp->len & (align - 1))) % align;
+               payload = skb_put(skb_aggr, skb_tmp->len + pad);
+               memcpy(payload, skb_tmp->data, skb_tmp->len);
+               if (skb_queue_empty(&port->tx_aggr.aggr_list)) {
+                       /* do not padding for last packet*/
+                       *(u16 *)payload = cpu_to_le16(skb_tmp->len);
+                       *(u16 *)&payload[2] =
+                               cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2 | 0x80);
+                       skb_trim(skb_aggr, skb_aggr->len - pad);
+               } else {
+                       /* add aggregation interface header */
+                       *(u16 *)payload = cpu_to_le16(skb_tmp->len + pad);
+                       *(u16 *)&payload[2] =
+                               cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2);
+               }
+
+               if (!is_txinfo_set) {
+                       tx_info->bss_num = MWIFIEX_SKB_TXCB(skb_tmp)->bss_num;
+                       tx_info->bss_type = MWIFIEX_SKB_TXCB(skb_tmp)->bss_type;
+                       is_txinfo_set = true;
+               }
+
+               port->tx_aggr.aggr_num--;
+               port->tx_aggr.aggr_len -= (skb_tmp->len + pad);
+               mwifiex_write_data_complete(adapter, skb_tmp, 0, 0);
+       }
+
+       tx_info->pkt_len = skb_aggr->len -
+                       (sizeof(struct txpd) + adapter->intf_hdr_len);
+       tx_info->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT;
+
+       port->tx_aggr.aggr_num = 0;
+       port->tx_aggr.aggr_len = 0;
+       *skb_send = skb_aggr;
+
+       return 0;
+}
+
+/* This function prepare data packet to be send under usb tx aggregation
+ * protocol, check current usb aggregation status, link packet to aggrgation
+ * list if possible, work flow as below:
+ * (1) if only 1 packet available, add usb tx aggregation header and send.
+ * (2) if packet is able to aggregated, link it to current aggregation list.
+ * (3) if packet is not able to aggregated, aggregate and send exist packets
+ *     in aggrgation list. Then, link packet in the list if there is more
+ *     packet in transmit queue, otherwise try to transmit single packet.
+ */
+static int mwifiex_usb_aggr_tx_data(struct mwifiex_adapter *adapter, u8 ep,
+                                   struct sk_buff *skb,
+                                   struct mwifiex_tx_param *tx_param,
+                                   struct usb_tx_data_port *port)
+{
+       u8 *payload, pad;
+       u16 align = adapter->bus_aggr.tx_aggr_align;
+       struct sk_buff *skb_send = NULL;
+       struct urb_context *context = NULL;
+       struct txpd *local_tx_pd =
+               (struct txpd *)((u8 *)skb->data + adapter->intf_hdr_len);
+       u8 f_send_aggr_buf = 0;
+       u8 f_send_cur_buf = 0;
+       u8 f_precopy_cur_buf = 0;
+       u8 f_postcopy_cur_buf = 0;
+       int ret;
+
+       /* padding to ensure each packet alginment */
+       pad = (align - (skb->len & (align - 1))) % align;
+
+       if (tx_param && tx_param->next_pkt_len) {
+               /* next packet available in tx queue*/
+               if (port->tx_aggr.aggr_len + skb->len + pad >
+                   adapter->bus_aggr.tx_aggr_max_size) {
+                       f_send_aggr_buf = 1;
+                       f_postcopy_cur_buf = 1;
+               } else {
+                       /* current packet could be aggregated*/
+                       f_precopy_cur_buf = 1;
+
+                       if (port->tx_aggr.aggr_len + skb->len + pad +
+                           tx_param->next_pkt_len >
+                           adapter->bus_aggr.tx_aggr_max_size ||
+                           port->tx_aggr.aggr_num + 2 >
+                           adapter->bus_aggr.tx_aggr_max_num) {
+                           /* next packet could not be aggregated
+                            * send current aggregation buffer
+                            */
+                               f_send_aggr_buf = 1;
+                       }
+               }
+       } else {
+               /* last packet in tx queue */
+               if (port->tx_aggr.aggr_num > 0) {
+                       /* pending packets in aggregation buffer*/
+                       if (port->tx_aggr.aggr_len + skb->len + pad >
+                           adapter->bus_aggr.tx_aggr_max_size) {
+                               /* current packet not be able to aggregated,
+                                * send aggr buffer first, then send packet.
+                                */
+                               f_send_cur_buf = 1;
+                       } else {
+                               /* last packet, Aggregation and send */
+                               f_precopy_cur_buf = 1;
+                       }
+
+                       f_send_aggr_buf = 1;
+               } else {
+                       /* no pending packets in aggregation buffer,
+                        * send current packet immediately
+                        */
+                        f_send_cur_buf = 1;
+               }
+       }
+
+       if (local_tx_pd->flags & MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET) {
+               /* Send NULL packet immediately*/
+               if (f_precopy_cur_buf) {
+                       if (skb_queue_empty(&port->tx_aggr.aggr_list)) {
+                               f_precopy_cur_buf = 0;
+                               f_send_aggr_buf = 0;
+                               f_send_cur_buf = 1;
+                       } else {
+                               f_send_aggr_buf = 1;
+                       }
+               } else if (f_postcopy_cur_buf) {
+                       f_send_cur_buf = 1;
+                       f_postcopy_cur_buf = 0;
+               }
+       }
+
+       if (f_precopy_cur_buf) {
+               skb_queue_tail(&port->tx_aggr.aggr_list, skb);
+               port->tx_aggr.aggr_len += (skb->len + pad);
+               port->tx_aggr.aggr_num++;
+       }
+
+       if (f_send_aggr_buf) {
+               ret = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send);
+               if (!ret) {
+                       context = &port->tx_data_list[port->tx_data_ix++];
+                       ret = mwifiex_usb_construct_send_urb(adapter, port, ep,
+                                                            context, skb_send);
+                       if (ret == -1)
+                               mwifiex_write_data_complete(adapter, skb_send,
+                                                           0, -1);
+               }
+       }
+
+       if (f_send_cur_buf) {
+               if (f_send_aggr_buf) {
+                       if (atomic_read(&port->tx_data_urb_pending) >=
+                           MWIFIEX_TX_DATA_URB) {
+                               port->block_status = true;
+                               adapter->data_sent =
+                                       mwifiex_usb_data_sent(adapter);
+                               /* no available urb, postcopy packet*/
+                               f_postcopy_cur_buf = 1;
+                               goto postcopy_cur_buf;
+                       }
+
+                       if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB)
+                               port->tx_data_ix = 0;
+               }
+
+               payload = skb->data;
+               *(u16 *)&payload[2] =
+                       cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2 | 0x80);
+               *(u16 *)payload = cpu_to_le16(skb->len);
+               skb_send = skb;
+               context = &port->tx_data_list[port->tx_data_ix++];
+               return mwifiex_usb_construct_send_urb(adapter, port, ep,
+                                                     context, skb_send);
+       }
+
+postcopy_cur_buf:
+       if (f_postcopy_cur_buf) {
+               skb_queue_tail(&port->tx_aggr.aggr_list, skb);
+               port->tx_aggr.aggr_len += (skb->len + pad);
+               port->tx_aggr.aggr_num++;
+       }
+
+       return -EINPROGRESS;
+}
+
+/* This function write a command/data packet to card. */
+static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep,
+                                   struct sk_buff *skb,
+                                   struct mwifiex_tx_param *tx_param)
+{
+       struct usb_card_rec *card = adapter->card;
+       struct urb_context *context = NULL;
+       struct usb_tx_data_port *port = NULL;
+       int idx;
+
+       if (adapter->is_suspended) {
+               mwifiex_dbg(adapter, ERROR,
+                           "%s: not allowed while suspended\n", __func__);
+               return -1;
+       }
+
+       if (adapter->surprise_removed) {
+               mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__);
+               return -1;
+       }
+
+       mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep);
+
+       if (ep == card->tx_cmd_ep) {
+               context = &card->tx_cmd;
+       } else {
+               /* get the data port structure for endpoint */
+               for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) {
+                       if (ep == card->port[idx].tx_data_ep) {
+                               port = &card->port[idx];
+                               if (atomic_read(&port->tx_data_urb_pending)
+                                   >= MWIFIEX_TX_DATA_URB) {
+                                       port->block_status = true;
+                                       adapter->data_sent =
+                                               mwifiex_usb_data_sent(adapter);
+                                       return -EBUSY;
+                               }
+                               if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB)
+                                       port->tx_data_ix = 0;
+                               break;
+                       }
+               }
+
+               if (!port) {
+                       mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n");
+                       return -1;
+               }
+
+               if (adapter->bus_aggr.enable)
+                       return mwifiex_usb_aggr_tx_data(adapter, ep, skb,
+                                                       tx_param, port);
+
+               context = &port->tx_data_list[port->tx_data_ix++];
+       }
+
+       return mwifiex_usb_construct_send_urb(adapter, port, ep, context, skb);
+}
+
 /* This function register usb device and initialize parameter. */
 static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 {
@@ -990,10 +1218,31 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
        return 0;
 }
 
+static void mwifiex_usb_cleanup_tx_aggr(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
+       struct usb_tx_data_port *port;
+       struct sk_buff *skb_tmp;
+       int idx;
+
+       if (adapter->bus_aggr.enable) {
+               for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) {
+                       port = &card->port[idx];
+                       while ((skb_tmp =
+                               skb_dequeue(&port->tx_aggr.aggr_list)))
+                               mwifiex_write_data_complete(adapter, skb_tmp,
+                                                           0, -1);
+               }
+       }
+}
+
 static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
 {
        struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
 
+       if (adapter->bus_aggr.enable)
+               mwifiex_usb_cleanup_tx_aggr(adapter);
+
        card->adapter = NULL;
 }
 
index e36bd63172ff9606ee61979fdb51a96330b996ef..b89b840e014208a1cb481255cb8e9ee717b88048 100644 (file)
@@ -64,12 +64,21 @@ struct urb_context {
        u8 ep;
 };
 
+struct usb_tx_aggr {
+       struct sk_buff_head aggr_list;
+       int aggr_len;
+       int aggr_num;
+};
+
 struct usb_tx_data_port {
        u8 tx_data_ep;
        u8 block_status;
        atomic_t tx_data_urb_pending;
        int tx_data_ix;
        struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB];
+       /* usb tx aggregation*/
+       struct usb_tx_aggr tx_aggr;
+       struct sk_buff *skb_aggr[MWIFIEX_TX_DATA_URB];
 };
 
 struct usb_card_rec {