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