From 887bc209d36a52cf0cd15b7042704caa02f00a88 Mon Sep 17 00:00:00 2001 From: Samuel Ahn Date: Tue, 17 Feb 2015 20:31:05 -0800 Subject: Implement OCB per-packet TX control header When wlan is in OCB mode, the user application can include a per-packet TX control header in front of the packet to control things like the power and MCS. The host driver will convert the TX control header to the equivalent HTT header and send the parameters to the wlan FW. Change-Id: Ib730eb1dcce26da84e7b48c772714fdad56c2683 CRs-Fixed: 754373 --- CORE/CLD_TXRX/TXRX/ol_tx.c | 58 +++++++++++- CORE/CLD_TXRX/TXRX/ol_tx_desc.c | 2 +- CORE/HDD/src/wlan_hdd_tx_rx.c | 38 -------- CORE/SERVICES/COMMON/ol_htt_tx_api.h | 178 ++++++++++++++++++++++++++++++++++- 4 files changed, 231 insertions(+), 45 deletions(-) diff --git a/CORE/CLD_TXRX/TXRX/ol_tx.c b/CORE/CLD_TXRX/TXRX/ol_tx.c index 20419839b69a..4de014f2d5eb 100644 --- a/CORE/CLD_TXRX/TXRX/ol_tx.c +++ b/CORE/CLD_TXRX/TXRX/ol_tx.c @@ -505,6 +505,48 @@ ol_tx_non_std_ll( /* tx filtering is handled within the target FW */ #define TX_FILTER_CHECK(tx_msdu_info) 0 /* don't filter */ + +/** + * parse_ocb_tx_header() - Function to check for OCB + * TX control header on a packet and extract it if present + * + * @msdu: Pointer to OS packet (adf_nbuf_t) + */ +#define HDD_ETHERTYPE_OCB_TX 0x8151 +#define OCB_HEADER_VERSION 1 +static bool parse_ocb_tx_header(adf_nbuf_t msdu, + struct ocb_tx_ctrl_hdr_t *tx_ctrl) +{ + struct ether_header *eth_hdr_p; + struct ocb_tx_ctrl_hdr_t *tx_ctrl_hdr; + + /* Check if TX control header is present */ + eth_hdr_p = (struct ether_header *) adf_nbuf_data(msdu); + if (eth_hdr_p->ether_type != adf_os_htons(HDD_ETHERTYPE_OCB_TX)) { + /* TX control header is not present. Nothing to do.. */ + return true; + } + + /* Remove the ethernet header */ + adf_nbuf_pull_head(msdu, sizeof(struct ether_header)); + + /* Parse the TX control header */ + tx_ctrl_hdr = (struct ocb_tx_ctrl_hdr_t*) adf_nbuf_data(msdu); + + if (tx_ctrl_hdr->version == OCB_HEADER_VERSION) { + if (tx_ctrl) { + adf_os_mem_copy(tx_ctrl, tx_ctrl_hdr, sizeof(*tx_ctrl_hdr)); + } + } else { + /* The TX control header is invalid. */ + return false; + } + + /* Remove the TX control header */ + adf_nbuf_pull_head(msdu, tx_ctrl_hdr->length); + return true; +} + static inline adf_nbuf_t ol_tx_hl_base( ol_txrx_vdev_handle vdev, @@ -515,6 +557,8 @@ ol_tx_hl_base( struct ol_txrx_pdev_t *pdev = vdev->pdev; adf_nbuf_t msdu = msdu_list; struct ol_txrx_msdu_info_t tx_msdu_info; + struct ocb_tx_ctrl_hdr_t tx_ctrl; + htt_pdev_handle htt_pdev = pdev->htt_pdev; tx_msdu_info.peer = NULL; @@ -529,6 +573,8 @@ ol_tx_hl_base( struct ol_tx_frms_queue_t *txq; struct ol_tx_desc_t *tx_desc = NULL; + adf_os_mem_zero(&tx_ctrl, sizeof(tx_ctrl)); + /* * The netbuf will get stored into a (peer-TID) tx queue list * inside the ol_tx_classify_store function or else dropped, @@ -586,6 +632,14 @@ ol_tx_hl_base( tx_msdu_info.htt.info.l2_hdr_type = pdev->htt_pkt_type; tx_msdu_info.htt.action.tx_comp_req = tx_comp_req; + /* If the vdev is in OCB mode, parse the tx control header. */ + if (vdev->opmode == wlan_op_mode_ocb) { + if (!parse_ocb_tx_header(msdu, &tx_ctrl)) { + /* There was an error parsing the header. Skip this packet. */ + goto MSDU_LOOP_BOTTOM; + } + } + txq = ol_tx_classify(vdev, tx_desc, msdu, &tx_msdu_info); if ((!txq) || TX_FILTER_CHECK(&tx_msdu_info)) { @@ -643,7 +697,7 @@ ol_tx_hl_base( tx_desc->htt_tx_desc_paddr, ol_tx_desc_id(pdev, tx_desc), msdu, - &tx_msdu_info.htt); + &tx_msdu_info.htt, &tx_ctrl, vdev->opmode == wlan_op_mode_ocb); /* * If debug display is enabled, show the meta-data being * downloaded to the target via the HTT tx descriptor. @@ -849,7 +903,7 @@ ol_txrx_mgmt_send( tx_desc->htt_tx_desc_paddr, ol_tx_desc_id(pdev, tx_desc), tx_mgmt_frm, - &tx_msdu_info.htt); + &tx_msdu_info.htt, NULL, 0); htt_tx_desc_display(tx_desc->htt_tx_desc); htt_tx_desc_set_chanfreq(tx_desc->htt_tx_desc, chanfreq); diff --git a/CORE/CLD_TXRX/TXRX/ol_tx_desc.c b/CORE/CLD_TXRX/TXRX/ol_tx_desc.c index 45fbc598fbd4..710732a4b6f2 100644 --- a/CORE/CLD_TXRX/TXRX/ol_tx_desc.c +++ b/CORE/CLD_TXRX/TXRX/ol_tx_desc.c @@ -200,7 +200,7 @@ ol_tx_desc_ll( tx_desc->htt_tx_desc_paddr, ol_tx_desc_id(pdev, tx_desc), netbuf, - &msdu_info->htt); + &msdu_info->htt, NULL, vdev->opmode == wlan_op_mode_ocb); /* * Initialize the fragmentation descriptor. diff --git a/CORE/HDD/src/wlan_hdd_tx_rx.c b/CORE/HDD/src/wlan_hdd_tx_rx.c index f9d9f491a41a..91cb6145eeff 100644 --- a/CORE/HDD/src/wlan_hdd_tx_rx.c +++ b/CORE/HDD/src/wlan_hdd_tx_rx.c @@ -741,40 +741,6 @@ void hdd_tx_resume_cb(void *adapter_context, } #endif /* QCA_LL_TX_FLOW_CT */ -/** - * hdd_remove_ocb_tx_header() - Function to check for OCB - * TX control header on a packet and extract it if present - * - * @skb: Pointer to OS packet (sk_buff) - */ -static void hdd_remove_ocb_tx_header(struct sk_buff *skb) -{ - struct ethhdr *eth_hdr_p; - struct ethhdr eth_hdr; - - /* Check if TX control header is present */ - eth_hdr_p = (struct ethhdr *) &skb->data[0]; - if (eth_hdr_p->h_proto != htons(HDD_ETHERTYPE_OCB_TX)) { - /* TX control header is not present. Nothing to do.. */ - return; - } - - /* Copy and remove the ethernet header */ - memcpy(ð_hdr, eth_hdr_p, HDD_ETH_HEADER_LEN); - skb_pull(skb, HDD_ETH_HEADER_LEN); - - /* Remove the TX control header */ - skb_pull(skb, HDD_OCB_TX_HEADER_LEN); - - /* Convert Ethernet header to 802.3 header by changing ethertype - field to length field */ - eth_hdr.h_proto = htons(skb->len); - - /* Push the now 802.3 header back */ - skb_push(skb, HDD_ETH_HEADER_LEN); - memcpy(&skb->data[0], ð_hdr, HDD_ETH_HEADER_LEN); -} - /**============================================================================ @brief hdd_hard_start_xmit() - Function registered with the Linux OS for transmitting packets. This version of the function directly passes the packet @@ -838,10 +804,6 @@ int hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) STAId = pHddStaCtx->conn_info.staId[0]; } - if (pAdapter->device_mode == WLAN_HDD_OCB) { - hdd_remove_ocb_tx_header(skb); - } - #ifdef QCA_LL_TX_FLOW_CT if (VOS_FALSE == WLANTL_GetTxResource((WLAN_HDD_GET_CTX(pAdapter))->pvosContext, diff --git a/CORE/SERVICES/COMMON/ol_htt_tx_api.h b/CORE/SERVICES/COMMON/ol_htt_tx_api.h index 06527bd594b3..7e208012333a 100644 --- a/CORE/SERVICES/COMMON/ol_htt_tx_api.h +++ b/CORE/SERVICES/COMMON/ol_htt_tx_api.h @@ -48,6 +48,22 @@ #include #include +/* Remove these macros when they get added to htt.h. */ +#ifndef HTT_TX_DESC_EXTENSION_GET +#define HTT_TX_DESC_EXTENSION_OFFSET_BYTES 0 +#define HTT_TX_DESC_EXTENSION_OFFSET_DWORD 0 +#define HTT_TX_DESC_EXTENSION_M 0x10000000 +#define HTT_TX_DESC_EXTENSION_S 28 + +#define HTT_TX_DESC_EXTENSION_GET(_var) \ + (((_var) & HTT_TX_DESC_EXTENSION_M) >> HTT_TX_DESC_EXTENSION_S) +#define HTT_TX_DESC_EXTENSION_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_TX_DESC_EXTENSION, _val); \ + ((_var) |= ((_val) << HTT_TX_DESC_EXTENSION_S)); \ + } while (0) +#endif + /*================ meta-info about tx MSDUs =================================*/ /* @@ -95,6 +111,111 @@ enum htt_frm_subtype { htt_frm_subtype_data_QoS_cf_ack_cf_poll = 15, }; +enum htt_ofdm_datarate { // Value MBPS Modulation Coding + htt_ofdm_datarate_6_mbps = 0, // 0 6 BPSK 1/2 + htt_ofdm_datarate_9_mbps = 1, // 1 9 BPSK 3/4 + htt_ofdm_datarate_12_mbps = 2, // 2 12 QPSK 1/2 + htt_ofdm_datarate_18_mbps = 3, // 3 18 QPSK 3/4 + htt_ofdm_datarate_24_mbps = 4, // 4 24 16-QAM 1/2 + htt_ofdm_datarate_36_mbps = 5, // 5 36 16-QAM 3/4 + htt_ofdm_datarate_48_mbps = 6, // 6 48 64-QAM 1/2 + htt_ofdm_datarate_54_mbps = 7, // 7 54 64-QAM 3/4 + htt_ofdm_datarate_max = 7, +}; + +/** + * @brief TX control header + * @details + * When sending an OCB packet, the user application has + * the option of including the following struct following an ethernet header + * with the proto field set to 0x8151. This struct includes various TX + * paramaters including the TX power and MCS. + */ +PREPACK struct ocb_tx_ctrl_hdr_t { + /* The version must be 1. */ + A_UINT16 version; + A_UINT16 length; + A_UINT16 channel_freq; + + /* flags */ + union { + struct { + A_UINT16 + /* bit 0: if set, tx pwr spec is valid */ + valid_pwr: 1, + /* bit 1: if set, tx MCS mask spec is valid */ + valid_datarate: 1, + /* bit 2: if set, tx retries spec is valid */ + valid_retries: 1, + /* bit 3: if set, chain mask is valid */ + valid_chain_mask: 1, + /* bit 4: if set, tx expire TSF spec is valid*/ + valid_expire_tsf: 1, + /* bit 5: if set, TID is valid */ + valid_tid: 1, + reserved0_15_6: 10; /* bits 15:6 - unused, set to 0x0 */ + }; + A_UINT16 all_flags; + }; + + /* TX expiry time (TSF) LSBs */ + A_UINT32 expire_tsf_lo; + + /* TX expiry time (TSF) MSBs */ + A_UINT32 expire_tsf_hi; + + /* pwr - + * Specify what power the tx frame needs to be transmitted at. + * The power a signed (two's complement) value is in units of 0.5 dBm. + * The value needs to be appropriately sign-extended when extracting + * the value from the message and storing it in a variable that is + * larger than A_INT8. + * If the transmission uses multiple tx chains, this power spec is + * the total transmit power, assuming incoherent combination of + * per-chain power to produce the total power. + */ + A_INT8 pwr; + + /* datarate - + * The desired modulation and coding scheme. + * + * VALUE DATA RATE MODULATION CODING RATE + * @ 20 MHz + * (MBPS) + * 0 6 BPSK 1/2 + * 1 9 BPSK 3/4 + * 2 12 QPSK 1/2 + * 3 18 QPSK 3/4 + * 4 24 16-QAM 1/2 + * 5 36 16-QAM 3/4 + * 6 48 64-QAM 1/2 + * 7 54 64-QAM 3/4 + */ + A_UINT8 datarate; + + /* retry_limit - + * Specify the maximum number of transmissions, including the + * initial transmission, to attempt before giving up if no ack + * is received. + * If the tx rate is specified, then all retries shall use the + * same rate as the initial transmission. + * If no tx rate is specified, the target can choose whether to + * retain the original rate during the retransmissions, or to + * fall back to a more robust rate. + */ + A_UINT8 retry_limit; + + /* Chain mask - specify which chains to transmit from. */ + A_UINT8 chain_mask; + + /* Extended Traffic ID (0-15) */ + A_UINT8 ext_tid; + + /* Ensure that the size of the structure is a multiple of 4. */ + A_UINT8 reserved[3]; + +} POSTPACK; + /** * @brief tx MSDU meta-data that HTT may use to program the FW/HW tx descriptor */ @@ -393,12 +514,15 @@ htt_tx_desc_init( u_int32_t htt_tx_desc_paddr_lo, u_int16_t msdu_id, adf_nbuf_t msdu, - struct htt_msdu_info_t *msdu_info) + struct htt_msdu_info_t *msdu_info, + struct ocb_tx_ctrl_hdr_t *tx_ctrl, + u_int8_t is_dsrc) { u_int32_t *word0, *word1, *word3; - u_int32_t local_word0, local_word1; + u_int32_t local_word0, local_word1, local_word3; struct htt_host_tx_desc_t *htt_host_tx_desc = (struct htt_host_tx_desc_t *) (((char *) htt_tx_desc) - HTT_TX_DESC_VADDR_OFFSET); + bool desc_ext_required = (tx_ctrl && tx_ctrl->all_flags != 0); word0 = (u_int32_t *) htt_tx_desc; word1 = word0 + 1; @@ -417,8 +541,13 @@ htt_tx_desc_init( HTT_H2T_MSG_TYPE_SET(local_word0, HTT_H2T_MSG_TYPE_TX_FRM); HTT_TX_DESC_PKT_TYPE_SET(local_word0, msdu_info->info.l2_hdr_type); HTT_TX_DESC_VDEV_ID_SET(local_word0, msdu_info->info.vdev_id); - HTT_TX_DESC_EXT_TID_SET(local_word0, msdu_info->info.ext_tid); + if (tx_ctrl && tx_ctrl->valid_tid) { + HTT_TX_DESC_EXT_TID_SET(local_word0, tx_ctrl->ext_tid); + } else { + HTT_TX_DESC_EXT_TID_SET(local_word0, msdu_info->info.ext_tid); + } HTT_TX_DESC_CKSUM_OFFLOAD_SET(local_word0, msdu_info->action.cksum_offload); + HTT_TX_DESC_EXTENSION_SET(local_word0, desc_ext_required); if (pdev->cfg.is_high_latency) HTT_TX_DESC_TX_COMP_SET(local_word0, msdu_info->action.tx_comp_req); HTT_TX_DESC_NO_ENCRYPT_SET(local_word0, msdu_info->action.do_encrypt ? 0 : 1); @@ -427,10 +556,51 @@ htt_tx_desc_init( local_word1 = 0; HTT_TX_DESC_FRM_LEN_SET(local_word1, adf_nbuf_len(msdu)); HTT_TX_DESC_FRM_ID_SET(local_word1, msdu_id); + *word1 = local_word1; /* Initialize peer_id to INVALID_PEER bcoz this is NOT Reinjection path*/ - *word3 = HTT_INVALID_PEER; + local_word3 = HTT_INVALID_PEER; + if (tx_ctrl && tx_ctrl->channel_freq) { + HTT_TX_DESC_CHAN_FREQ_SET(local_word3, tx_ctrl->channel_freq); + } + *word3 = local_word3; + + /* + * If any of the tx control flags are set, then we need the extended + * HTT header. + */ + if (desc_ext_required) + { + struct htt_tx_msdu_desc_ext_t local_desc_ext = {0}; + + /* + * Copy the info that was read from TX control header from the user + * application to the extended HTT header. + * First copy everything + * to a local temp structure, and then copy everything to the + * actual uncached structure in one go to save memory writes. + */ + local_desc_ext.valid_pwr = tx_ctrl->valid_pwr; + local_desc_ext.valid_mcs_mask = tx_ctrl->valid_datarate; + local_desc_ext.valid_retries = tx_ctrl->valid_retries; + local_desc_ext.valid_expire_tsf = tx_ctrl->valid_expire_tsf; + local_desc_ext.valid_chainmask = tx_ctrl->valid_chain_mask; + + local_desc_ext.pwr = tx_ctrl->pwr; + if (tx_ctrl->valid_datarate && + tx_ctrl->datarate <= htt_ofdm_datarate_max) + local_desc_ext.mcs_mask = (1 << (tx_ctrl->datarate + 4)); + local_desc_ext.retry_limit = tx_ctrl->retry_limit; + local_desc_ext.expire_tsf_lo = tx_ctrl->expire_tsf_lo; + local_desc_ext.expire_tsf_hi = tx_ctrl->expire_tsf_hi; + local_desc_ext.chain_mask = tx_ctrl->chain_mask; + + local_desc_ext.is_dsrc = (is_dsrc != 0); + + adf_nbuf_push_head(msdu, sizeof(local_desc_ext)); + adf_os_mem_copy(adf_nbuf_data(msdu), &local_desc_ext, + sizeof(local_desc_ext)); } /* * Specify that the data provided by the OS is a bytestream, -- cgit v1.2.3