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 priv->stats.txmulticast += pstx_status->txmcok; 340 priv->stats.txbroadcast += pstx_status->txbcok; 341 priv->stats.txunicast += pstx_status->txucok; 342 343 priv->stats.txerrmulticast += pstx_status->txmcfail; 344 priv->stats.txerrbroadcast += pstx_status->txbcfail; 345 priv->stats.txerrunicast += pstx_status->txucfail; 346 347 priv->stats.txbytesmulticast += pstx_status->txmclength; 348 priv->stats.txbytesbroadcast += pstx_status->txbclength; 349 priv->stats.txbytesunicast += pstx_status->txuclength; 350 351 priv->stats.last_packet_rate = pstx_status->rate; 352 } 353 354 /*----------------------------------------------------------------------------- 355 * Function: cmpk_handle_tx_status() 356 * 357 * Overview: Firmware add a new tx feedback status to reduce rx command 358 * packet buffer operation load. 359 * 360 * Input: NONE 361 * 362 * Output: NONE 363 * 364 * Return: NONE 365 * 366 * Revised History: 367 * When Who Remark 368 * 05/12/2008 amy Create Version 0 porting from windows code. 369 * 370 *--------------------------------------------------------------------------- 371 */ 372 static void cmpk_handle_tx_status(struct net_device *dev, u8 *pmsg) 373 { 374 cmpk_tx_status_t rx_tx_sts; 375 376 memcpy((void *)&rx_tx_sts, (void *)pmsg, sizeof(cmpk_tx_status_t)); 377 /* 2. Use tx feedback info to count TX statistics. */ 378 cmpk_count_tx_status(dev, &rx_tx_sts); 379 } 380 381 /*----------------------------------------------------------------------------- 382 * Function: cmpk_handle_tx_rate_history() 383 * 384 * Overview: Firmware add a new tx rate history 385 * 386 * Input: NONE 387 * 388 * Output: NONE 389 * 390 * Return: NONE 391 * 392 * Revised History: 393 * When Who Remark 394 * 05/12/2008 amy Create Version 0 porting from windows code. 395 * 396 *--------------------------------------------------------------------------- 397 */ 398 static void cmpk_handle_tx_rate_history(struct net_device *dev, u8 *pmsg) 399 { 400 cmpk_tx_rahis_t *ptxrate; 401 u8 i, j; 402 u16 length = sizeof(cmpk_tx_rahis_t); 403 u32 *ptemp; 404 struct r8192_priv *priv = ieee80211_priv(dev); 405 406 #ifdef ENABLE_PS 407 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, 408 (pu1Byte)(&rtState)); 409 410 /* When RF is off, we should not count the packet for hw/sw synchronize 411 * reason, ie. there may be a duration while sw switch is changed and 412 * hw switch is being changed. 413 */ 414 if (rtState == eRfOff) 415 return; 416 #endif 417 418 ptemp = (u32 *)pmsg; 419 420 /* Do endian transfer to word alignment(16 bits) for windows system. 421 * You must do different endian transfer for linux and MAC OS 422 */ 423 for (i = 0; i < (length/4); i++) { 424 u16 temp1, temp2; 425 426 temp1 = ptemp[i] & 0x0000FFFF; 427 temp2 = ptemp[i] >> 16; 428 ptemp[i] = (temp1 << 16) | temp2; 429 } 430 431 ptxrate = (cmpk_tx_rahis_t *)pmsg; 432 433 if (!ptxrate) 434 return; 435 436 for (i = 0; i < 16; i++) { 437 /* Collect CCK rate packet num */ 438 if (i < 4) 439 priv->stats.txrate.cck[i] += ptxrate->cck[i]; 440 441 /* Collect OFDM rate packet num */ 442 if (i < 8) 443 priv->stats.txrate.ofdm[i] += ptxrate->ofdm[i]; 444 445 for (j = 0; j < 4; j++) 446 priv->stats.txrate.ht_mcs[j][i] += ptxrate->ht_mcs[j][i]; 447 } 448 } 449 450 /*----------------------------------------------------------------------------- 451 * Function: cmpk_message_handle_rx() 452 * 453 * Overview: In the function, we will capture different RX command packet 454 * info. Every RX command packet element has different message 455 * length and meaning in content. We only support three type of RX 456 * command packet now. Please refer to document 457 * ws-06-0063-rtl8190-command-packet-specification. 458 * 459 * Input: NONE 460 * 461 * Output: NONE 462 * 463 * Return: NONE 464 * 465 * Revised History: 466 * When Who Remark 467 * 05/06/2008 amy Create Version 0 porting from windows code. 468 * 469 *--------------------------------------------------------------------------- 470 */ 471 u32 cmpk_message_handle_rx(struct net_device *dev, 472 struct ieee80211_rx_stats *pstats) 473 { 474 int total_length; 475 u8 cmd_length, exe_cnt = 0; 476 u8 element_id; 477 u8 *pcmd_buff; 478 479 /* 0. Check inpt arguments. It is a command queue message or 480 * pointer is null. 481 */ 482 if (!pstats) 483 return 0; /* This is not a command packet. */ 484 485 /* 1. Read received command packet message length from RFD. */ 486 total_length = pstats->Length; 487 488 /* 2. Read virtual address from RFD. */ 489 pcmd_buff = pstats->virtual_address; 490 491 /* 3. Read command packet element id and length. */ 492 element_id = pcmd_buff[0]; 493 494 /* 4. Check every received command packet content according to different 495 * element type. Because FW may aggregate RX command packet to 496 * minimize transmit time between DRV and FW. 497 */ 498 /* Add a counter to prevent the lock in the loop from being held too 499 * long 500 */ 501 while (total_length > 0 && exe_cnt++ < 100) { 502 /* We support aggregation of different cmd in the same packet */ 503 element_id = pcmd_buff[0]; 504 505 switch (element_id) { 506 case RX_TX_FEEDBACK: 507 cmpk_handle_tx_feedback(dev, pcmd_buff); 508 cmd_length = CMPK_RX_TX_FB_SIZE; 509 break; 510 511 case RX_INTERRUPT_STATUS: 512 cmpk_handle_interrupt_status(dev, pcmd_buff); 513 cmd_length = sizeof(struct cmd_pkt_interrupt_status); 514 break; 515 516 case BOTH_QUERY_CONFIG: 517 cmpk_handle_query_config_rx(dev, pcmd_buff); 518 cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE; 519 break; 520 521 case RX_TX_STATUS: 522 cmpk_handle_tx_status(dev, pcmd_buff); 523 cmd_length = CMPK_RX_TX_STS_SIZE; 524 break; 525 526 case RX_TX_PER_PKT_FEEDBACK: 527 /* You must at lease add a switch case element here, 528 * Otherwise, we will jump to default case. 529 */ 530 cmd_length = CMPK_RX_TX_FB_SIZE; 531 break; 532 533 case RX_TX_RATE_HISTORY: 534 cmpk_handle_tx_rate_history(dev, pcmd_buff); 535 cmd_length = CMPK_TX_RAHIS_SIZE; 536 break; 537 538 default: 539 540 RT_TRACE(COMP_ERR, "---->%s():unknown CMD Element\n", 541 __func__); 542 return 1; /* This is a command packet. */ 543 } 544 545 total_length -= cmd_length; 546 pcmd_buff += cmd_length; 547 } 548 return 1; /* This is a command packet. */ 549 } 550