77 | | |
78 | | |
79 | | |
| 78 | The most significant change to the MAC Low Framework is the handling of the {{{IPC_MBOX_TX_MPDU_READY}}} message. In the default codebase, that message will directly lead to calling the {{{frame_tx_callback()}}} callback function pointer. In TokenMAC, we need to be able to defer a transmission until later if it currently is not our token reservation period. So, instead of calling {{{frame_tx_callback()}}} directly within the IPC handling of {{{IPC_MBOX_TX_MPDU_READY}}}, we move everything out of that case statement into a new function: |
| 79 | |
| 80 | {{{ |
| 81 | #!c |
| 82 | |
| 83 | void wlan_mac_low_proc_pkt_buf(u16 tx_pkt_buf){ |
| 84 | u32 status; |
| 85 | tx_frame_info * tx_mpdu; |
| 86 | mac_header_80211 * tx_80211_header; |
| 87 | u8 rate; |
| 88 | u16 ACK_N_DBPS; |
| 89 | u32 isLocked, owner; |
| 90 | u32 low_tx_details_size; |
| 91 | wlan_ipc_msg ipc_msg_to_high; |
| 92 | |
| 93 | if(lock_pkt_buf_tx(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ |
| 94 | warp_printf(PL_ERROR, "Error: unable to lock TX pkt_buf %d\n", tx_pkt_buf); |
| 95 | |
| 96 | status_pkt_buf_tx(tx_pkt_buf, &isLocked, &owner); |
| 97 | |
| 98 | warp_printf(PL_ERROR, " TX pkt_buf %d status: isLocked = %d, owner = %d\n", tx_pkt_buf, isLocked, owner); |
| 99 | |
| 100 | } else { |
| 101 | |
| 102 | tx_mpdu = (tx_frame_info*)TX_PKT_BUF_TO_ADDR(tx_pkt_buf); |
| 103 | |
| 104 | tx_mpdu->delay_accept = (u32)(get_usec_timestamp() - tx_mpdu->timestamp_create); |
| 105 | |
| 106 | //Convert rate index into rate code used in PHY's SIGNAL field |
| 107 | //ACK_N_DBPS is used to calculate duration of received ACKs. |
| 108 | //The selection of ACK rates given DATA rates is specified in 9.7.6.5.2 of 802.11-2012 |
| 109 | rate = wlan_mac_mcs_to_phy_rate(tx_mpdu->params.phy.rate); |
| 110 | ACK_N_DBPS = wlan_mac_mcs_to_n_dbps(wlan_mac_mcs_to_ctrl_resp_mcs(tx_mpdu->params.phy.rate)); |
| 111 | |
| 112 | if((tx_mpdu->flags) & TX_MPDU_FLAGS_FILL_DURATION){ |
| 113 | //Get pointer to start of MAC header in packet buffer |
| 114 | tx_80211_header = (mac_header_80211*)(TX_PKT_BUF_TO_ADDR(tx_pkt_buf)+PHY_TX_PKT_BUF_MPDU_OFFSET); |
| 115 | |
| 116 | //Compute and fill in the duration of any time-on-air following this packet's transmission |
| 117 | // For DATA Tx, DURATION = T_SIFS + T_ACK, where T_ACK is function of the ACK Tx rate |
| 118 | tx_80211_header->duration_id = wlan_ofdm_txtime(sizeof(mac_header_80211_ACK)+WLAN_PHY_FCS_NBYTES, ACK_N_DBPS) + T_SIFS; |
| 119 | } |
| 120 | |
| 121 | if((tx_mpdu->flags) & TX_MPDU_FLAGS_FILL_TIMESTAMP){ |
| 122 | //Some management packets contain the node's local 64-bit microsecond timer value |
| 123 | // The Tx hardware can insert this value into the outgoing byte stream automatically |
| 124 | // This ensures the timestamp value is not skewed by any pre-Tx deferrals |
| 125 | |
| 126 | //The macros below set the first and last byte index where the Tx logic should insert |
| 127 | // the 8-byte timestamp. |
| 128 | //In the current implementation these indexes must span an 8-byte-aligned |
| 129 | // region of the packet buffer (i.e. (start_ind % 8)==0 ) |
| 130 | wlan_phy_tx_timestamp_ins_start((24+PHY_TX_PKT_BUF_PHY_HDR_SIZE)); |
| 131 | wlan_phy_tx_timestamp_ins_end((31+PHY_TX_PKT_BUF_PHY_HDR_SIZE)); |
| 132 | |
| 133 | } else { |
| 134 | //When start>end, the Tx logic will not insert any timestamp |
| 135 | wlan_phy_tx_timestamp_ins_start(1); |
| 136 | wlan_phy_tx_timestamp_ins_end(0); |
| 137 | } |
| 138 | |
| 139 | //Submit the MPDU for transmission - this callback will return only when the MPDU Tx is |
| 140 | // complete (after all re-transmissions, ACK Rx, timeouts, etc.) |
| 141 | |
| 142 | status = frame_tx_callback(tx_pkt_buf, rate, tx_mpdu->length, low_tx_details); |
| 143 | |
| 144 | if((tx_mpdu->flags) & TX_MPDU_FLAGS_FILL_TIMESTAMP){ |
| 145 | //The Tx logic automatically inserted the timestamp at the time that the bytes |
| 146 | //were being fed out to the Tx PHY. We can go back and re-insert this time into the |
| 147 | //payload so that further processing (e.g. logging) sees the correct payload. |
| 148 | |
| 149 | //First, calculate what the value should be |
| 150 | |
| 151 | *((u64*)( (TX_PKT_BUF_TO_ADDR(tx_pkt_buf)+PHY_TX_PKT_BUF_MPDU_OFFSET + 24)) ) = (u64) ( (u64)get_tx_start_timestamp() + (s64)wlan_mac_get_timestamp_offset() ); |
| 152 | } |
| 153 | |
| 154 | //Record the total time this MPDU spent in the Tx state machine |
| 155 | tx_mpdu->delay_done = (u32)(get_usec_timestamp() - (tx_mpdu->timestamp_create + (u64)(tx_mpdu->delay_accept))); |
| 156 | |
| 157 | low_tx_details_size = (tx_mpdu->num_tx_attempts)*sizeof(wlan_mac_low_tx_details); |
| 158 | |
| 159 | if(status == TX_MPDU_RESULT_SUCCESS){ |
| 160 | tx_mpdu->tx_result = TX_MPDU_RESULT_SUCCESS; |
| 161 | } else { |
| 162 | tx_mpdu->tx_result = TX_MPDU_RESULT_FAILURE; |
| 163 | } |
| 164 | |
| 165 | //Revert the state of the packet buffer and return control to CPU High |
| 166 | if(unlock_pkt_buf_tx(tx_pkt_buf) != PKT_BUF_MUTEX_SUCCESS){ |
| 167 | warp_printf(PL_ERROR, "Error: unable to unlock TX pkt_buf %d\n", tx_pkt_buf); |
| 168 | wlan_mac_low_send_exception(EXC_MUTEX_TX_FAILURE); |
| 169 | } else { |
| 170 | ipc_msg_to_high.msg_id = IPC_MBOX_MSG_ID(IPC_MBOX_TX_MPDU_DONE); |
| 171 | |
| 172 | //Add the per-Tx-event details to the IPC message so CPU High can add them to the log as TX_LOW entries |
| 173 | if(low_tx_details != NULL){ |
| 174 | ipc_msg_to_high.payload_ptr = (u32*)low_tx_details; |
| 175 | |
| 176 | //Make sure we don't overfill the IPC mailbox with TX_LOW data; truncate the Tx details if necessary |
| 177 | if(low_tx_details_size < (IPC_BUFFER_MAX_NUM_WORDS << 2)){ |
| 178 | ipc_msg_to_high.num_payload_words = ( low_tx_details_size ) >> 2; // # of u32 words |
| 179 | } else { |
| 180 | ipc_msg_to_high.num_payload_words = ( ((IPC_BUFFER_MAX_NUM_WORDS << 2)/sizeof(wlan_mac_low_tx_details) )*sizeof(wlan_mac_low_tx_details) ) >> 2; // # of u32 words |
| 181 | } |
| 182 | } else { |
| 183 | ipc_msg_to_high.num_payload_words = 0; |
| 184 | ipc_msg_to_high.payload_ptr = NULL; |
| 185 | } |
| 186 | ipc_msg_to_high.arg0 = tx_pkt_buf; |
| 187 | ipc_mailbox_write_msg(&ipc_msg_to_high); |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | }}} |
| 192 | |
| 193 | Now we will change the {{{IPC_MBOX_TX_MPDU_READY}}} case to the following: |
| 194 | |
| 195 | {{{ |
| 196 | #!c |
| 197 | |
| 198 | case IPC_MBOX_TX_MPDU_READY: |
| 199 | if(allow_new_mpdu_tx){ |
| 200 | wlan_mac_low_proc_pkt_buf( msg->arg0 ); |
| 201 | } else { |
| 202 | pkt_buf_pending_tx = msg->arg0; |
| 203 | } |
| 204 | break; |
| 205 | }}} |
| 206 | |
| 207 | Basically, we will rely on the new {{{allow_new_mpdu_tx}}} global variable to tell us whether or not we are allowed to process a new MPDU for transmission. If we are, we will call the new {{{wlan_mac_low_proc_pkt_buf}}} function just like before. If we are not allowed to transmit, we will store the pending packet buffer to the new {{{pkt_buf_pending_tx}}} global variable and worry about calling {{{wlan_mac_low_proc_pkt_buf}}} later when we are allowed to transmit once again. |
| 208 | |
| 209 | ---- |
| 210 | |
| 211 | |