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