1/****************************************************************************** 2 * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. 3 * Linux device driver for RTL8192U 4 * 5 * This program is distributed in the hope that it will be useful, but WITHOUT 6 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 7 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 8 * more details. 9 * 10 * You should have received a copy of the GNU General Public License along with 11 * this program; if not, write to the Free Software Foundation, Inc., 12 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 13 * 14 * The full GNU General Public License is included in this distribution in the 15 * file called LICENSE. 16 * 17 * Contact Information: 18 * wlanfae <wlanfae@realtek.com> 19******************************************************************************/ 20#include "r8192U.h" 21#include "r819xU_cmdpkt.h" 22 23bool SendTxCommandPacket(struct net_device *dev, void *pData, u32 DataLen) 24{ 25 bool rtStatus = true; 26 struct r8192_priv *priv = ieee80211_priv(dev); 27 struct sk_buff *skb; 28 cb_desc *tcb_desc; 29 unsigned char *ptr_buf; 30 31 /* 32 * Get TCB and local buffer from common pool. 33 * (It is shared by CmdQ, MgntQ, and USB coalesce DataQ) 34 */ 35 skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + DataLen + 4); 36 if (!skb) 37 return false; 38 memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); 39 tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); 40 tcb_desc->queue_index = TXCMD_QUEUE; 41 tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_NORMAL; 42 tcb_desc->bLastIniPkt = 0; 43 skb_reserve(skb, USB_HWDESC_HEADER_LEN); 44 ptr_buf = skb_put(skb, DataLen); 45 memcpy(ptr_buf, pData, DataLen); 46 tcb_desc->txbuf_size = (u16)DataLen; 47 48 if (!priv->ieee80211->check_nic_enough_desc(dev, tcb_desc->queue_index) || 49 (!skb_queue_empty(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index])) || 50 (priv->ieee80211->queue_stop)) { 51 RT_TRACE(COMP_FIRMWARE, "NULL packet => tx full\n"); 52 skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb); 53 } else { 54 priv->ieee80211->softmac_hard_start_xmit(skb, dev); 55 } 56 57 return rtStatus; 58} 59 60/* 61 * Function: cmpk_message_handle_tx() 62 * 63 * Overview: Driver internal module can call the API to send message to 64 * firmware side. For example, you can send a debug command packet. 65 * Or you can send a request for FW to modify RLX4181 LBUS HW bank. 66 * Otherwise, you can change MAC/PHT/RF register by firmware at 67 * run time. We do not support message more than one segment now. 68 * 69 * Input: NONE 70 * 71 * Output: NONE 72 * 73 * Return: NONE 74 */ 75 extern bool cmpk_message_handle_tx( 76 struct net_device *dev, 77 u8 *codevirtualaddress, 78 u32 packettype, 79 u32 buffer_len) 80{ 81 bool rt_status = true; 82 return rt_status; 83} 84 85/* 86 * Function: cmpk_counttxstatistic() 87 */ 88static void 89cmpk_count_txstatistic(struct net_device *dev, cmpk_txfb_t *pstx_fb) 90{ 91 struct r8192_priv *priv = ieee80211_priv(dev); 92#ifdef ENABLE_PS 93 RT_RF_POWER_STATE rtState; 94 95 pAdapter->HalFunc.GetHwRegHandler(pAdapter, 96 HW_VAR_RF_STATE, 97 (pu1Byte)(&rtState)); 98 99 /* 100 * When RF is off, we should not count the packet for hw/sw synchronize 101 * reason, ie. there may be a duration while sw switch is changed and hw 102 * switch is being changed. 103 */ 104 if (rtState == eRfOff) 105 return; 106#endif 107 108#ifdef TODO 109 if (pAdapter->bInHctTest) 110 return; 111#endif 112 /* 113 * We can not know the packet length and transmit type: 114 * broadcast or uni or multicast. 115 * So the relative statistics must be collected in tx feedback info 116 */ 117 if (pstx_fb->tok) { 118 priv->stats.txfeedbackok++; 119 priv->stats.txoktotal++; 120 priv->stats.txokbytestotal += pstx_fb->pkt_length; 121 priv->stats.txokinperiod++; 122 /* We can not make sure broadcast/multicast or unicast mode. */ 123 if (pstx_fb->pkt_type == PACKET_MULTICAST) { 124 priv->stats.txmulticast++; 125 priv->stats.txbytesmulticast += pstx_fb->pkt_length; 126 } else if (pstx_fb->pkt_type == PACKET_BROADCAST) { 127 priv->stats.txbroadcast++; 128 priv->stats.txbytesbroadcast += pstx_fb->pkt_length; 129 } else { 130 priv->stats.txunicast++; 131 priv->stats.txbytesunicast += pstx_fb->pkt_length; 132 } 133 } else { 134 priv->stats.txfeedbackfail++; 135 priv->stats.txerrtotal++; 136 priv->stats.txerrbytestotal += pstx_fb->pkt_length; 137 /* We can not make sure broadcast/multicast or unicast mode. */ 138 if (pstx_fb->pkt_type == PACKET_MULTICAST) 139 priv->stats.txerrmulticast++; 140 else if (pstx_fb->pkt_type == PACKET_BROADCAST) 141 priv->stats.txerrbroadcast++; 142 else 143 priv->stats.txerrunicast++; 144 } 145 priv->stats.txretrycount += pstx_fb->retry_cnt; 146 priv->stats.txfeedbackretry += pstx_fb->retry_cnt; 147} 148 149/* 150 * Function: cmpk_handle_tx_feedback() 151 * 152 * Overview: The function is responsible for extract the message inside TX 153 * feedbck message from firmware. It will contain dedicated info in 154 * ws-06-0063-rtl8190-command-packet-specification. Please 155 * refer to chapter "TX Feedback Element". We have to read 20 bytes 156 * in the command packet. 157 * 158 * Input: struct net_device * dev 159 * u8 *pmsg - Msg Ptr of the command packet. 160 * 161 * Output: NONE 162 * 163 * Return: NONE 164 */ 165static void cmpk_handle_tx_feedback(struct net_device *dev, u8 *pmsg) 166{ 167 struct r8192_priv *priv = ieee80211_priv(dev); 168 cmpk_txfb_t rx_tx_fb; 169 170 priv->stats.txfeedback++; 171 172 /* 1. Extract TX feedback info from RFD to temp structure buffer. */ 173 memcpy((u8 *)&rx_tx_fb, pmsg, sizeof(cmpk_txfb_t)); 174 175 /* 2. Use tx feedback info to count TX statistics. */ 176 cmpk_count_txstatistic(dev, &rx_tx_fb); 177} 178 179void cmdpkt_beacontimerinterrupt_819xusb(struct net_device *dev) 180{ 181 struct r8192_priv *priv = ieee80211_priv(dev); 182 u16 tx_rate; 183 184 if (priv->ieee80211->current_network.mode == IEEE_A || 185 priv->ieee80211->current_network.mode == IEEE_N_5G || 186 (priv->ieee80211->current_network.mode == IEEE_N_24G && 187 (!priv->ieee80211->pHTInfo->bCurSuppCCK))) { 188 tx_rate = 60; 189 DMESG("send beacon frame tx rate is 6Mbpm\n"); 190 } else { 191 tx_rate = 10; 192 DMESG("send beacon frame tx rate is 1Mbpm\n"); 193 } 194 rtl819xusb_beacon_tx(dev, tx_rate); /* HW Beacon */ 195} 196 197/* 198 * Function: cmpk_handle_interrupt_status() 199 * 200 * Overview: The function is responsible for extract the message from 201 * firmware. It will contain dedicated info in 202 * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc. 203 * Please refer to chapter "Interrupt Status Element". 204 * 205 * Input: struct net_device *dev, 206 * u8* pmsg - Message Pointer of the command packet. 207 * 208 * Output: NONE 209 * 210 * Return: NONE 211 */ 212static void cmpk_handle_interrupt_status(struct net_device *dev, u8 *pmsg) 213{ 214 cmpk_intr_sta_t rx_intr_status; /* */ 215 struct r8192_priv *priv = ieee80211_priv(dev); 216 217 DMESG("---> cmpk_Handle_Interrupt_Status()\n"); 218 219 /* 1. Extract TX feedback info from RFD to temp structure buffer. */ 220 rx_intr_status.length = pmsg[1]; 221 if (rx_intr_status.length != (sizeof(cmpk_intr_sta_t) - 2)) { 222 DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n"); 223 return; 224 } 225 /* Statistics of beacon for ad-hoc mode. */ 226 if (priv->ieee80211->iw_mode == IW_MODE_ADHOC) { 227 //2 maybe need endian transform? 228 rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4)); 229 230 DMESG("interrupt status = 0x%x\n", rx_intr_status.interrupt_status); 231 232 if (rx_intr_status.interrupt_status & ISR_TxBcnOk) { 233 priv->ieee80211->bibsscoordinator = true; 234 priv->stats.txbeaconokint++; 235 } else if (rx_intr_status.interrupt_status & ISR_TxBcnErr) { 236 priv->ieee80211->bibsscoordinator = false; 237 priv->stats.txbeaconerr++; 238 } 239 240 if (rx_intr_status.interrupt_status & ISR_BcnTimerIntr) 241 cmdpkt_beacontimerinterrupt_819xusb(dev); 242 } 243 /* Other informations in interrupt status we need? */ 244 DMESG("<---- cmpk_handle_interrupt_status()\n"); 245} 246 247/* 248 * Function: cmpk_handle_query_config_rx() 249 * 250 * Overview: The function is responsible for extract the message from 251 * firmware. It will contain dedicated info in 252 * ws-06-0063-rtl8190-command-packet-specification 253 * Please refer to chapter "Beacon State Element". 254 * 255 * Input: u8 * pmsg - Message Pointer of the command packet. 256 * 257 * Output: NONE 258 * 259 * Return: NONE 260 * 261 */ 262static void cmpk_handle_query_config_rx(struct net_device *dev, u8 *pmsg) 263{ 264 cmpk_query_cfg_t rx_query_cfg; 265 /* 266 * Extract TX feedback info from RFD to temp structure buffer. 267 */ 268 rx_query_cfg.cfg_action = (pmsg[4] & 0x80000000) >> 31; 269 rx_query_cfg.cfg_type = (pmsg[4] & 0x60) >> 5; 270 rx_query_cfg.cfg_size = (pmsg[4] & 0x18) >> 3; 271 rx_query_cfg.cfg_page = (pmsg[6] & 0x0F) >> 0; 272 rx_query_cfg.cfg_offset = pmsg[7]; 273 rx_query_cfg.value = (pmsg[8] << 24) | (pmsg[9] << 16) | 274 (pmsg[10] << 8) | (pmsg[11] << 0); 275 rx_query_cfg.mask = (pmsg[12] << 24) | (pmsg[13] << 16) | 276 (pmsg[14] << 8) | (pmsg[15] << 0); 277} 278 279/* 280 * Function: cmpk_count_tx_status() 281 * 282 * Overview: Count aggregated tx status from firmware of one type rx command 283 * packet element id = RX_TX_STATUS. 284 * 285 * Input: NONE 286 * 287 * Output: NONE 288 * 289 * Return: NONE 290 */ 291static void cmpk_count_tx_status(struct net_device *dev, 292 cmpk_tx_status_t *pstx_status) 293{ 294 struct r8192_priv *priv = ieee80211_priv(dev); 295 296#ifdef ENABLE_PS 297 298 RT_RF_POWER_STATE rtstate; 299 300 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState)); 301 302 /* 303 * When RF is off, we should not count the packet for hw/sw synchronize 304 * reason, ie. there may be a duration while sw switch is changed and hw 305 * switch is being changed. 306 */ 307 if (rtState == eRfOff) 308 return; 309#endif 310 311 priv->stats.txfeedbackok += pstx_status->txok; 312 priv->stats.txoktotal += pstx_status->txok; 313 314 priv->stats.txfeedbackfail += pstx_status->txfail; 315 priv->stats.txerrtotal += pstx_status->txfail; 316 317 priv->stats.txretrycount += pstx_status->txretry; 318 priv->stats.txfeedbackretry += pstx_status->txretry; 319 320 priv->stats.txmulticast += pstx_status->txmcok; 321 priv->stats.txbroadcast += pstx_status->txbcok; 322 priv->stats.txunicast += pstx_status->txucok; 323 324 priv->stats.txerrmulticast += pstx_status->txmcfail; 325 priv->stats.txerrbroadcast += pstx_status->txbcfail; 326 priv->stats.txerrunicast += pstx_status->txucfail; 327 328 priv->stats.txbytesmulticast += pstx_status->txmclength; 329 priv->stats.txbytesbroadcast += pstx_status->txbclength; 330 priv->stats.txbytesunicast += pstx_status->txuclength; 331 332 priv->stats.last_packet_rate = pstx_status->rate; 333} 334 335/* 336 * Function: cmpk_handle_tx_status() 337 * 338 * Overview: Firmware add a new tx feedback status to reduce rx command 339 * packet buffer operation load. 340 * 341 * Input: NONE 342 * 343 * Output: NONE 344 * 345 * Return: NONE 346 */ 347static void 348cmpk_handle_tx_status(struct net_device *dev, u8 *pmsg) 349{ 350 cmpk_tx_status_t rx_tx_sts; 351 352 memcpy((void *)&rx_tx_sts, (void *)pmsg, sizeof(cmpk_tx_status_t)); 353 /* 2. Use tx feedback info to count TX statistics. */ 354 cmpk_count_tx_status(dev, &rx_tx_sts); 355} 356 357/* 358 * Function: cmpk_handle_tx_rate_history() 359 * 360 * Overview: Firmware add a new tx rate history 361 * 362 * Input: NONE 363 * 364 * Output: NONE 365 * 366 * Return: NONE 367 */ 368static void cmpk_handle_tx_rate_history(struct net_device *dev, u8 *pmsg) 369{ 370 cmpk_tx_rahis_t *ptxrate; 371 u8 i, j; 372 u16 length = sizeof(cmpk_tx_rahis_t); 373 u32 *ptemp; 374 struct r8192_priv *priv = ieee80211_priv(dev); 375 376#ifdef ENABLE_PS 377 pAdapter->HalFunc.GetHwRegHandler(pAdapter, 378 HW_VAR_RF_STATE, 379 (pu1Byte)(&rtState)); 380 /* 381 * When RF is off, we should not count the packet for hw/sw synchronize 382 * reason, ie. there may be a duration while sw switch is changed and hw 383 * switch is being changed. 384 */ 385 if (rtState == eRfOff) 386 return; 387#endif 388 ptemp = (u32 *)pmsg; 389 390 /* 391 * Do endian transfer to word alignment(16 bits) for windows system. 392 * You must do different endian transfer for linux and MAC OS 393 */ 394 for (i = 0; i < (length/4); i++) { 395 u16 temp1, temp2; 396 temp1 = ptemp[i] & 0x0000FFFF; 397 temp2 = ptemp[i] >> 16; 398 ptemp[i] = (temp1 << 16) | temp2; 399 } 400 401 ptxrate = (cmpk_tx_rahis_t *)pmsg; 402 403 if (ptxrate == NULL) 404 return; 405 406 for (i = 0; i < 16; i++) { 407 /* Collect CCK rate packet num */ 408 if (i < 4) 409 priv->stats.txrate.cck[i] += ptxrate->cck[i]; 410 /* Collect OFDM rate packet num */ 411 if (i < 8) 412 priv->stats.txrate.ofdm[i] += ptxrate->ofdm[i]; 413 for (j = 0; j < 4; j++) 414 priv->stats.txrate.ht_mcs[j][i] += ptxrate->ht_mcs[j][i]; 415 } 416 417} 418 419/* 420 * Function: cmpk_message_handle_rx() 421 * 422 * Overview: In the function, we will capture different RX command packet 423 * info. Every RX command packet element has different message 424 * length and meaning in content. We only support three type of RX 425 * command packet now. Please refer to document 426 * ws-06-0063-rtl8190-command-packet-specification. 427 * 428 * Input: NONE 429 * 430 * Output: NONE 431 * 432 * Return: NONE 433 */ 434extern u32 435cmpk_message_handle_rx( 436 struct net_device *dev, 437 struct ieee80211_rx_stats *pstats) 438{ 439 struct r8192_priv *priv = ieee80211_priv(dev); 440 int total_length; 441 u8 cmd_length, exe_cnt = 0; 442 u8 element_id; 443 u8 *pcmd_buff; 444 445 /* 446 * 0. Check input arguments. 447 * If is is a command queue message or pointer is null 448 */ 449 if ((pstats == NULL)) 450 return 0; /* This is not a command packet. */ 451 452 /* 1. Read received command packet message length from RFD. */ 453 total_length = pstats->Length; 454 455 /* 2. Read virtual address from RFD. */ 456 pcmd_buff = pstats->virtual_address; 457 458 /* 3. Read command pakcet element id and length. */ 459 element_id = pcmd_buff[0]; 460 461 /* 462 * 4. Check every received command packet conent according to different 463 * element type. Because FW may aggregate RX command packet to minimize 464 * transmit time between DRV and FW. 465 */ 466 467 /* Add a counter to prevent to locked in the loop too long */ 468 while (total_length > 0 || exe_cnt++ > 100) { 469 /* We support aggregation of different cmd in the same packet */ 470 element_id = pcmd_buff[0]; 471 switch (element_id) { 472 case RX_TX_FEEDBACK: 473 cmpk_handle_tx_feedback(dev, pcmd_buff); 474 cmd_length = CMPK_RX_TX_FB_SIZE; 475 break; 476 case RX_INTERRUPT_STATUS: 477 cmpk_handle_interrupt_status(dev, pcmd_buff); 478 cmd_length = sizeof(cmpk_intr_sta_t); 479 break; 480 case BOTH_QUERY_CONFIG: 481 cmpk_handle_query_config_rx(dev, pcmd_buff); 482 cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE; 483 break; 484 case RX_TX_STATUS: 485 cmpk_handle_tx_status(dev, pcmd_buff); 486 cmd_length = CMPK_RX_TX_STS_SIZE; 487 break; 488 case RX_TX_PER_PKT_FEEDBACK: 489 cmd_length = CMPK_RX_TX_FB_SIZE; 490 break; 491 case RX_TX_RATE_HISTORY: 492 cmpk_handle_tx_rate_history(dev, pcmd_buff); 493 cmd_length = CMPK_TX_RAHIS_SIZE; 494 break; 495 case RX_TX_TSSI_MEAN_BACK: 496 { 497 u32 *pMsg; 498 pMsg = (u32 *)pcmd_buff; 499 } 500 cmd_length = 32; 501 break; 502 default: 503 RT_TRACE(COMP_ERR, "(%s): unknown CMD Element\n", 504 __func__); 505 return 1; /* This is a command packet. */ 506 } 507 priv->stats.rxcmdpkt[element_id]++; 508 total_length -= cmd_length; 509 pcmd_buff += cmd_length; 510 } 511 return 1; /* This is a command packet. */ 512} 513