| /** @file mlan_11n_aggr.c |
| * |
| * @brief This file contains functions for 11n Aggregation. |
| * |
| * Copyright (C) 2008-2009, Marvell International Ltd. |
| * All Rights Reserved |
| */ |
| |
| /******************************************************** |
| Change log: |
| 11/10/2008: initial version |
| ********************************************************/ |
| |
| #include "mlan.h" |
| #include "mlan_join.h" |
| #include "mlan_util.h" |
| #include "mlan_fw.h" |
| #include "mlan_main.h" |
| #include "mlan_wmm.h" |
| #include "mlan_11n.h" |
| #include "mlan_11n_aggr.h" |
| #include "mlan_sdio.h" |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| |
| /** |
| * @brief Aggregate individual packets into one AMSDU packet |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param amsdu_buf A pointer to packet buffer |
| * @param data A pointer to aggregated data packet being formed |
| * @param pkt_len Length of current packet to aggregate |
| * @param pad Pad |
| * |
| * @return Final packet size |
| */ |
| static int |
| wlan_11n_form_amsdu_pkt(pmlan_adapter pmadapter, t_u8 * amsdu_buf, t_u8 * data, |
| int pkt_len, int *pad) |
| { |
| int dt_offset, amsdu_buf_offset; |
| Rfc1042Hdr_t snap = { |
| 0xaa, /* LLC DSAP */ |
| 0xaa, /* LLC SSAP */ |
| 0x03, /* LLC CTRL */ |
| {0x00, 0x00, 0x00}, /* SNAP OUI */ |
| 0x0000 /* SNAP type */ |
| /* |
| * This field will be overwritten |
| * later with ethertype |
| */ |
| }; |
| |
| ENTER(); |
| |
| memcpy(amsdu_buf, data, (MLAN_MAC_ADDR_LENGTH) * 2); |
| dt_offset = amsdu_buf_offset = (MLAN_MAC_ADDR_LENGTH) * 2; |
| |
| snap.snap_type = *(t_u16 *) (data + dt_offset); |
| dt_offset += sizeof(t_u16); |
| *(t_u16 *) (amsdu_buf + amsdu_buf_offset) = mlan_htons(pkt_len + |
| LLC_SNAP_LEN - |
| ((2 * |
| MLAN_MAC_ADDR_LENGTH) |
| + sizeof(t_u16))); |
| amsdu_buf_offset += sizeof(t_u16); |
| memcpy(amsdu_buf + amsdu_buf_offset, &snap, LLC_SNAP_LEN); |
| amsdu_buf_offset += LLC_SNAP_LEN; |
| |
| memcpy(amsdu_buf + amsdu_buf_offset, data + dt_offset, pkt_len - dt_offset); |
| *pad = (((pkt_len + LLC_SNAP_LEN) & 3)) ? |
| (4 - (((pkt_len + LLC_SNAP_LEN)) & 3)) : 0; |
| |
| LEAVE(); |
| return (pkt_len + LLC_SNAP_LEN + *pad); |
| } |
| |
| /** |
| * @brief Add TxPD to AMSDU header |
| * |
| * @param priv A pointer to mlan_private structure |
| * @param mbuf Pointer to buffer where the TxPD will be formed |
| * |
| * @return N/A |
| */ |
| static void |
| wlan_11n_form_amsdu_txpd(mlan_private * priv, mlan_buffer * mbuf) |
| { |
| TxPD *ptx_pd; |
| mlan_adapter *pmadapter = priv->adapter; |
| ENTER(); |
| |
| ptx_pd = (TxPD *) mbuf->pbuf; |
| memset(ptx_pd, 0, sizeof(TxPD)); |
| |
| /* |
| * Original priority has been overwritten |
| */ |
| ptx_pd->priority = (t_u8) mbuf->priority; |
| ptx_pd->pkt_delay_2ms = wlan_wmm_compute_driver_packet_delay(priv, mbuf); |
| ptx_pd->bss_num = (t_u8) mbuf->bss_num; |
| if (pmadapter->ps_state != PS_STATE_AWAKE) { |
| if (MTRUE == wlan_check_last_packet_indication(priv)) { |
| pmadapter->tx_lock_flag = MTRUE; |
| ptx_pd->flags = MRVDRV_TxPD_POWER_MGMT_LAST_PACKET; |
| } |
| } |
| /* Always zero as the data is followed by TxPD */ |
| ptx_pd->tx_pkt_offset = sizeof(TxPD); |
| ptx_pd->tx_pkt_type = PKT_TYPE_AMSDU; |
| |
| if (ptx_pd->tx_control == 0) |
| /* TxCtrl set by user or default */ |
| ptx_pd->tx_control = priv->pkt_tx_ctrl; |
| |
| endian_convert_TxPD(ptx_pd); |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Update the TxPktLength field in TxPD after the complete AMSDU |
| * packet is formed |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param mbuf TxPD buffer |
| * |
| * @return N/A |
| */ |
| static INLINE void |
| wlan_11n_update_pktlen_amsdu_txpd(pmlan_adapter pmadapter, pmlan_buffer mbuf) |
| { |
| TxPD *ptx_pd; |
| |
| ENTER(); |
| |
| ptx_pd = (TxPD *) mbuf->pbuf; |
| ptx_pd->tx_pkt_length = |
| (t_u16) wlan_cpu_to_le16(mbuf->data_len - sizeof(TxPD)); |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Get number of aggregated packets |
| * |
| * @param data A pointer to packet data |
| * @param total_pkt_len Total packet length |
| * |
| * @return Number of packets |
| */ |
| static int |
| wlan_11n_get_num_aggrpkts(t_u8 * data, int total_pkt_len) |
| { |
| int pkt_count = 0, pkt_len, pad; |
| |
| while (total_pkt_len > 0) { |
| /* Length will be in network format, change it to host */ |
| pkt_len = mlan_ntohs((*(t_u16 *) (data + (2 * MLAN_MAC_ADDR_LENGTH)))); |
| pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? |
| (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : 0; |
| data += pkt_len + pad + sizeof(Eth803Hdr_t); |
| total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); |
| ++pkt_count; |
| } |
| |
| return pkt_count; |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| |
| /** |
| * @brief Deaggregate the received AMSDU packet |
| * |
| * @param priv A pointer to mlan_private structure |
| * @param pmbuf A pointer to aggregated data packet |
| * |
| * @return MLAN_STATUS_SUCCESS --success, otherwise fail |
| */ |
| mlan_status |
| wlan_11n_deaggregate_pkt(mlan_private * priv, pmlan_buffer pmbuf) |
| { |
| t_u16 pkt_len; |
| int total_pkt_len; |
| t_u8 *data; |
| int pad; |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| RxPacketHdr_t *prx_pkt; |
| mlan_buffer *daggr_mbuf; |
| mlan_adapter *pmadapter = priv->adapter; |
| t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = { 0xaa, 0xaa, 0x03, |
| 0x00, 0x00, 0x00 |
| }; |
| |
| ENTER(); |
| |
| data = (t_u8 *) (pmbuf->pbuf + pmbuf->data_offset); |
| total_pkt_len = pmbuf->data_len; |
| |
| /* Sanity test */ |
| if (total_pkt_len > MLAN_RX_DATA_BUF_SIZE) { |
| PRINTM(MERROR, "Total packet length greater than tx buffer" |
| " size %d\n", total_pkt_len); |
| goto done; |
| } |
| |
| pmbuf->use_count = wlan_11n_get_num_aggrpkts(data, total_pkt_len); |
| |
| while (total_pkt_len > 0) { |
| prx_pkt = (RxPacketHdr_t *) data; |
| /* Length will be in network format, change it to host */ |
| pkt_len = mlan_ntohs((*(t_u16 *) (data + (2 * MLAN_MAC_ADDR_LENGTH)))); |
| if (pkt_len > total_pkt_len) { |
| PRINTM(MERROR, "Error in pkt_len %d %d\n", total_pkt_len, pkt_len); |
| break; |
| } |
| |
| pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? |
| (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : 0; |
| |
| total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); |
| |
| if (memcmp(&prx_pkt->rfc1042_hdr, |
| rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { |
| memmove(data + LLC_SNAP_LEN, data, (2 * MLAN_MAC_ADDR_LENGTH)); |
| data += LLC_SNAP_LEN; |
| pkt_len += sizeof(Eth803Hdr_t) - LLC_SNAP_LEN; |
| } else { |
| *(t_u16 *) (data + (2 * MLAN_MAC_ADDR_LENGTH)) |
| = (t_u16) 0; |
| pkt_len += sizeof(Eth803Hdr_t); |
| } |
| |
| if ((pmadapter->callbacks.moal_malloc(sizeof(mlan_buffer), |
| (t_u8 **) & daggr_mbuf))) { |
| PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| memcpy(daggr_mbuf, pmbuf, sizeof(mlan_buffer)); |
| |
| daggr_mbuf->data_offset = 0; |
| daggr_mbuf->data_len = pkt_len; |
| daggr_mbuf->pbuf = data; |
| daggr_mbuf->pdesc = MNULL; |
| daggr_mbuf->pparent = pmbuf; |
| daggr_mbuf->priority = pmbuf->priority; |
| ret = |
| pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, |
| daggr_mbuf); |
| |
| switch (ret) { |
| case MLAN_STATUS_PENDING: |
| break; |
| case MLAN_STATUS_FAILURE: |
| PRINTM(MERROR, "Deaggr, send to moal failed\n"); |
| case MLAN_STATUS_SUCCESS: |
| wlan_recv_packet_complete(pmadapter, daggr_mbuf, ret); |
| break; |
| default: |
| break; |
| } |
| |
| data += pkt_len + pad; |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Aggregate multiple packets into one single AMSDU packet |
| * |
| * @param priv A pointer to mlan_private structure |
| * @param pra_list Pointer to the RA List table containing the pointers |
| * to packets. |
| * @param headroom Any interface specific headroom that may be need. TxPD |
| * will be formed leaving this headroom. |
| * @param ptrindex Pointer index |
| * |
| * @return Final packet size |
| */ |
| int |
| wlan_11n_aggregate_pkt(mlan_private * priv, raListTbl * pra_list, |
| int headroom, int ptrindex) |
| { |
| int pkt_size = 0; |
| pmlan_adapter pmadapter = priv->adapter; |
| mlan_buffer *pmbuf_aggr, *pmbuf_src; |
| t_u8 *data; |
| int pad = 0; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u32 sec, usec; |
| mlan_tx_param tx_param; |
| ENTER(); |
| |
| PRINTM(MDAT_D, "Handling Aggr packet\n"); |
| |
| if ((pmbuf_src = (pmlan_buffer) util_peek_list(&pra_list->buf_head, |
| pmadapter->callbacks. |
| moal_spin_lock, |
| pmadapter->callbacks. |
| moal_spin_unlock))) { |
| |
| if (! |
| (pmbuf_aggr = |
| wlan_alloc_mlan_buffer(&pmadapter->callbacks, |
| pmadapter->tx_buf_size))) { |
| PRINTM(MERROR, "Error allocating mlan_buffer\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| data = pmbuf_aggr->pbuf + headroom; |
| memcpy(pmbuf_aggr, pmbuf_src, sizeof(mlan_buffer)); |
| |
| pmbuf_aggr->pbuf = data; |
| pmbuf_aggr->pdesc = MNULL; |
| pmbuf_aggr->data_offset = 0; |
| |
| /* Form AMSDU */ |
| wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr); |
| pkt_size = sizeof(TxPD); |
| } else { |
| goto exit; |
| } |
| |
| while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN) |
| + headroom) |
| <= pmadapter->tx_buf_size)) { |
| |
| pmbuf_src = (pmlan_buffer) |
| util_dequeue_list(&pra_list->buf_head, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| |
| pra_list->total_pkts_size -= pmbuf_src->data_len; |
| |
| pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock); |
| pkt_size += wlan_11n_form_amsdu_pkt(pmadapter, |
| (data + pkt_size), |
| pmbuf_src->pbuf + |
| pmbuf_src->data_offset, |
| pmbuf_src->data_len, &pad); |
| |
| DBG_HEXDUMP(MDAT_D, "pmbuf_src", pmbuf_src, sizeof(mlan_buffer)); |
| pmadapter->callbacks.moal_send_packet_complete(pmadapter->pmoal_handle, |
| pmbuf_src, |
| MLAN_STATUS_SUCCESS); |
| |
| pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock); |
| |
| if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { |
| pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| pmbuf_src = (pmlan_buffer) util_peek_list(&pra_list->buf_head, |
| pmadapter->callbacks. |
| moal_spin_lock, |
| pmadapter->callbacks. |
| moal_spin_unlock); |
| } |
| |
| pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock); |
| |
| /* Last AMSDU packet does not need padding */ |
| pkt_size -= pad; |
| pmbuf_aggr->data_len = pkt_size; |
| wlan_11n_update_pktlen_amsdu_txpd(pmadapter, pmbuf_aggr); |
| pmbuf_aggr->data_len += headroom; |
| pmbuf_aggr->pbuf = data - headroom; |
| tx_param.next_pkt_len = ((pra_list->total_pkts_size) ? |
| (((pra_list->total_pkts_size) > |
| pmadapter->tx_buf_size) ? pmadapter-> |
| tx_buf_size : pra_list->total_pkts_size + |
| LLC_SNAP_LEN + sizeof(TxPD)) : 0); |
| ret = |
| wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf_aggr, |
| &tx_param); |
| switch (ret) { |
| case MLAN_STATUS_RESOURCE: |
| pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock); |
| |
| if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { |
| pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock); |
| wlan_write_data_complete(pmadapter, pmbuf_aggr, |
| MLAN_STATUS_FAILURE); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| util_enqueue_list_head(&pra_list->buf_head, |
| (pmlan_linked_list) pmbuf_aggr, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| |
| pra_list->total_pkts_size += pmbuf_aggr->data_len; |
| |
| pmbuf_aggr->flags = MLAN_BUF_FLAG_REQUEUED_PKT; |
| pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock); |
| PRINTM(MDATA, "MLAN_STATUS_RESOURCE is returned\n"); |
| break; |
| case MLAN_STATUS_FAILURE: |
| pmadapter->data_sent = MFALSE; |
| PRINTM(MERROR, "Error: moal_write_data_async failed: 0x%X\n", ret); |
| pmadapter->dbg.num_tx_host_to_card_failure++; |
| wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); |
| goto exit; |
| case MLAN_STATUS_PENDING: |
| pmadapter->data_sent = MFALSE; |
| break; |
| case MLAN_STATUS_SUCCESS: |
| wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); |
| break; |
| default: |
| break; |
| } |
| if (ret != MLAN_STATUS_RESOURCE) { |
| pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock); |
| if (wlan_is_ralist_valid(priv, pra_list, ptrindex)) { |
| priv->wmm.packets_out[ptrindex]++; |
| priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; |
| } |
| pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = |
| pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur->pnext; |
| pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock); |
| } |
| pmadapter->callbacks.moal_get_system_time(&sec, &usec); |
| PRINTM(MDATA, "%lu.%lu : Data => FW\n", sec, usec); |
| |
| exit: |
| LEAVE(); |
| return (pkt_size + headroom); |
| } |