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