1 /******************************************************************************
3 * (c) Copyright 2008, RealTEK Technologies Inc. All Rights Reserved.
5 * Module: r819xusb_cmdpkt.c
6 * (RTL8190 TX/RX command packet handler Source C File)
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
22 * 05/06/2008 amy Create initial version porting from
25 ******************************************************************************/
27 #include "r819xU_cmdpkt.h"
29 rt_status SendTxCommandPacket(struct net_device *dev, void *pData, u32 DataLen)
31 rt_status rtStatus = RT_STATUS_SUCCESS;
32 struct r8192_priv *priv = ieee80211_priv(dev);
35 unsigned char *ptr_buf;
37 /* Get TCB and local buffer from common pool.
38 (It is shared by CmdQ, MgntQ, and USB coalesce DataQ) */
39 skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + DataLen + 4);
41 return RT_STATUS_FAILURE;
42 memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
43 tcb_desc = (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 ptr_buf = skb_put(skb, DataLen);
49 memcpy(ptr_buf, pData, DataLen);
50 tcb_desc->txbuf_size = (u16)DataLen;
52 if (!priv->ieee80211->check_nic_enough_desc(dev, tcb_desc->queue_index) ||
53 (!skb_queue_empty(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index])) ||
54 (priv->ieee80211->queue_stop)) {
55 RT_TRACE(COMP_FIRMWARE, "=== NULL packet ======> tx full!\n");
56 skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb);
58 priv->ieee80211->softmac_hard_start_xmit(skb, dev);
64 /*-----------------------------------------------------------------------------
65 * Function: cmpk_message_handle_tx()
67 * Overview: Driver internal module can call the API to send message to
68 * firmware side. For example, you can send a debug command packet.
69 * Or you can send a request for FW to modify RLX4181 LBUS HW bank.
70 * Otherwise, you can change MAC/PHT/RF register by firmware at
71 * run time. We do not support message more than one segment now.
81 * 05/06/2008 amy porting from windows code.
83 *---------------------------------------------------------------------------*/
84 extern rt_status cmpk_message_handle_tx(struct net_device *dev,
85 u8 *codevirtualaddress,
86 u32 packettype, u32 buffer_len)
89 bool rt_status = true;
93 struct r8192_priv *priv = ieee80211_priv(dev);
95 u16 frag_length, frag_offset = 0;
97 rt_firmware *pfirmware = priv->pFirmware;
99 unsigned char *seg_ptr;
103 firmware_init_param(dev);
104 /* Fragmentation might be required */
105 frag_threshold = pfirmware->cmdpacket_frag_thresold;
107 if ((buffer_len - frag_offset) > frag_threshold) {
108 frag_length = frag_threshold;
112 frag_length = buffer_len - frag_offset;
117 /* Allocate skb buffer to contain firmware info and tx
118 descriptor info add 4 to avoid packet appending overflow. */
120 skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + frag_length + 4);
122 skb = dev_alloc_skb(frag_length + 4);
124 memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
125 tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
126 tcb_desc->queue_index = TXCMD_QUEUE;
127 tcb_desc->bCmdOrInit = packettype;
128 tcb_desc->bLastIniPkt = bLastIniPkt;
131 skb_reserve(skb, USB_HWDESC_HEADER_LEN);
134 seg_ptr = skb_put(skb, buffer_len);
136 * Transform from little endian to big endian
139 memcpy(seg_ptr, codevirtualaddress, buffer_len);
140 tcb_desc->txbuf_size = (u16)buffer_len;
143 if (!priv->ieee80211->check_nic_enough_desc(dev, tcb_desc->queue_index) ||
144 (!skb_queue_empty(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index])) ||
145 (priv->ieee80211->queue_stop)) {
146 RT_TRACE(COMP_FIRMWARE, "======> tx full!\n");
147 skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb);
149 priv->ieee80211->softmac_hard_start_xmit(skb, dev);
152 codevirtualaddress += frag_length;
153 frag_offset += frag_length;
155 } while (frag_offset < buffer_len);
163 /*-----------------------------------------------------------------------------
164 * Function: cmpk_counttxstatistic()
168 * Input: PADAPTER pAdapter
169 * CMPK_TXFB_T *psTx_FB
177 * 05/12/2008 amy Create Version 0 porting from windows code.
179 *---------------------------------------------------------------------------*/
180 static void cmpk_count_txstatistic(struct net_device *dev, cmpk_txfb_t *pstx_fb)
182 struct r8192_priv *priv = ieee80211_priv(dev);
184 RT_RF_POWER_STATE rtState;
186 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
187 (pu1Byte)(&rtState));
189 /* When RF is off, we should not count the packet for hw/sw synchronize
190 reason, ie. there may be a duration while sw switch is changed and
191 hw switch is being changed. */
192 if (rtState == eRfOff)
197 if (pAdapter->bInHctTest)
200 /* We can not know the packet length and transmit type:
201 broadcast or uni or multicast. So the relative statistics
202 must be collected in tx feedback info. */
204 priv->stats.txfeedbackok++;
205 priv->stats.txoktotal++;
206 priv->stats.txokbytestotal += pstx_fb->pkt_length;
207 priv->stats.txokinperiod++;
209 /* We can not make sure broadcast/multicast or unicast mode. */
210 if (pstx_fb->pkt_type == PACKET_MULTICAST) {
211 priv->stats.txmulticast++;
212 priv->stats.txbytesmulticast += pstx_fb->pkt_length;
213 } else if (pstx_fb->pkt_type == PACKET_BROADCAST) {
214 priv->stats.txbroadcast++;
215 priv->stats.txbytesbroadcast += pstx_fb->pkt_length;
217 priv->stats.txunicast++;
218 priv->stats.txbytesunicast += pstx_fb->pkt_length;
221 priv->stats.txfeedbackfail++;
222 priv->stats.txerrtotal++;
223 priv->stats.txerrbytestotal += pstx_fb->pkt_length;
225 /* We can not make sure broadcast/multicast or unicast mode. */
226 if (pstx_fb->pkt_type == PACKET_MULTICAST)
227 priv->stats.txerrmulticast++;
228 else if (pstx_fb->pkt_type == PACKET_BROADCAST)
229 priv->stats.txerrbroadcast++;
231 priv->stats.txerrunicast++;
234 priv->stats.txretrycount += pstx_fb->retry_cnt;
235 priv->stats.txfeedbackretry += pstx_fb->retry_cnt;
241 /*-----------------------------------------------------------------------------
242 * Function: cmpk_handle_tx_feedback()
244 * Overview: The function is responsible for extract the message inside TX
245 * feedbck message from firmware. It will contain dedicated info in
246 * ws-06-0063-rtl8190-command-packet-specification.
247 * Please refer to chapter "TX Feedback Element".
248 * We have to read 20 bytes in the command packet.
250 * Input: struct net_device *dev
251 * u8 *pmsg - Msg Ptr of the command packet.
259 * 05/08/2008 amy Create Version 0 porting from windows code.
261 *---------------------------------------------------------------------------*/
262 static void cmpk_handle_tx_feedback(struct net_device *dev, u8 *pmsg)
264 struct r8192_priv *priv = ieee80211_priv(dev);
265 cmpk_txfb_t rx_tx_fb;
267 priv->stats.txfeedback++;
269 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
270 /* It seems that FW use big endian(MIPS) and DRV use little endian in
271 windows OS. So we have to read the content byte by byte or transfer
272 endian type before copy the message copy. */
273 /* Use pointer to transfer structure memory. */
274 memcpy((u8 *)&rx_tx_fb, pmsg, sizeof(cmpk_txfb_t));
275 /* 2. Use tx feedback info to count TX statistics. */
276 cmpk_count_txstatistic(dev, &rx_tx_fb);
277 /* Comment previous method for TX statistic function. */
278 /* Collect info TX feedback packet to fill TCB. */
279 /* We can not know the packet length and transmit type: broadcast or uni
284 void cmdpkt_beacontimerinterrupt_819xusb(struct net_device *dev)
286 struct r8192_priv *priv = ieee80211_priv(dev);
288 /* 87B have to S/W beacon for DTM encryption_cmn. */
289 if (priv->ieee80211->current_network.mode == IEEE_A ||
290 priv->ieee80211->current_network.mode == IEEE_N_5G ||
291 (priv->ieee80211->current_network.mode == IEEE_N_24G &&
292 (!priv->ieee80211->pHTInfo->bCurSuppCCK))) {
294 DMESG("send beacon frame tx rate is 6Mbpm\n");
297 DMESG("send beacon frame tx rate is 1Mbpm\n");
300 rtl819xusb_beacon_tx(dev, tx_rate); /* HW Beacon */
308 /*-----------------------------------------------------------------------------
309 * Function: cmpk_handle_interrupt_status()
311 * Overview: The function is responsible for extract the message from
312 * firmware. It will contain dedicated info in
313 * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc.
314 * Please refer to chapter "Interrupt Status Element".
316 * Input: struct net_device *dev
317 * u8 *pmsg - Message Pointer of the command packet.
325 * 05/12/2008 amy Add this for rtl8192 porting from windows code.
327 *---------------------------------------------------------------------------*/
328 static void cmpk_handle_interrupt_status(struct net_device *dev, u8 *pmsg)
330 cmpk_intr_sta_t rx_intr_status; /* */
331 struct r8192_priv *priv = ieee80211_priv(dev);
333 DMESG("---> cmpk_Handle_Interrupt_Status()\n");
335 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
336 /* It seems that FW use big endian(MIPS) and DRV use little endian in
337 windows OS. So we have to read the content byte by byte or transfer
338 endian type before copy the message copy. */
339 rx_intr_status.length = pmsg[1];
340 if (rx_intr_status.length != (sizeof(cmpk_intr_sta_t) - 2)) {
341 DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
346 /* Statistics of beacon for ad-hoc mode. */
347 if (priv->ieee80211->iw_mode == IW_MODE_ADHOC) {
348 /* 2 maybe need endian transform? */
349 rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4));
351 DMESG("interrupt status = 0x%x\n",
352 rx_intr_status.interrupt_status);
354 if (rx_intr_status.interrupt_status & ISR_TxBcnOk) {
355 priv->ieee80211->bibsscoordinator = true;
356 priv->stats.txbeaconokint++;
357 } else if (rx_intr_status.interrupt_status & ISR_TxBcnErr) {
358 priv->ieee80211->bibsscoordinator = false;
359 priv->stats.txbeaconerr++;
362 if (rx_intr_status.interrupt_status & ISR_BcnTimerIntr)
363 cmdpkt_beacontimerinterrupt_819xusb(dev);
367 /* Other informations in interrupt status we need? */
370 DMESG("<---- cmpk_handle_interrupt_status()\n");
375 /*-----------------------------------------------------------------------------
376 * Function: cmpk_handle_query_config_rx()
378 * Overview: The function is responsible for extract the message from
379 * firmware. It will contain dedicated info in
380 * ws-06-0063-rtl8190-command-packet-specification. Please
381 * refer to chapter "Beacon State Element".
383 * Input: u8 *pmsg - Message Pointer of the command packet.
391 * 05/12/2008 amy Create Version 0 porting from windows code.
393 *---------------------------------------------------------------------------*/
394 static void cmpk_handle_query_config_rx(struct net_device *dev, u8 *pmsg)
396 cmpk_query_cfg_t rx_query_cfg;
399 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
400 /* It seems that FW use big endian(MIPS) and DRV use little endian in
401 windows OS. So we have to read the content byte by byte or transfer
402 endian type before copy the message copy. */
403 rx_query_cfg.cfg_action = (pmsg[4] & 0x80000000) >> 31;
404 rx_query_cfg.cfg_type = (pmsg[4] & 0x60) >> 5;
405 rx_query_cfg.cfg_size = (pmsg[4] & 0x18) >> 3;
406 rx_query_cfg.cfg_page = (pmsg[6] & 0x0F) >> 0;
407 rx_query_cfg.cfg_offset = pmsg[7];
408 rx_query_cfg.value = (pmsg[8] << 24) | (pmsg[9] << 16) |
409 (pmsg[10] << 8) | (pmsg[11] << 0);
410 rx_query_cfg.mask = (pmsg[12] << 24) | (pmsg[13] << 16) |
411 (pmsg[14] << 8) | (pmsg[15] << 0);
416 /*-----------------------------------------------------------------------------
417 * Function: cmpk_count_tx_status()
419 * Overview: Count aggregated tx status from firmwar of one type rx command
420 * packet element id = RX_TX_STATUS.
430 * 05/12/2008 amy Create Version 0 porting from windows code.
432 *---------------------------------------------------------------------------*/
433 static void cmpk_count_tx_status(struct net_device *dev,
434 cmpk_tx_status_t *pstx_status)
436 struct r8192_priv *priv = ieee80211_priv(dev);
440 RT_RF_POWER_STATE rtstate;
442 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
443 (pu1Byte)(&rtState));
445 /* When RF is off, we should not count the packet for hw/sw synchronize
446 reason, ie. there may be a duration while sw switch is changed and
447 hw switch is being changed. */
448 if (rtState == eRfOff)
452 priv->stats.txfeedbackok += pstx_status->txok;
453 priv->stats.txoktotal += pstx_status->txok;
455 priv->stats.txfeedbackfail += pstx_status->txfail;
456 priv->stats.txerrtotal += pstx_status->txfail;
458 priv->stats.txretrycount += pstx_status->txretry;
459 priv->stats.txfeedbackretry += pstx_status->txretry;
462 priv->stats.txmulticast += pstx_status->txmcok;
463 priv->stats.txbroadcast += pstx_status->txbcok;
464 priv->stats.txunicast += pstx_status->txucok;
466 priv->stats.txerrmulticast += pstx_status->txmcfail;
467 priv->stats.txerrbroadcast += pstx_status->txbcfail;
468 priv->stats.txerrunicast += pstx_status->txucfail;
470 priv->stats.txbytesmulticast += pstx_status->txmclength;
471 priv->stats.txbytesbroadcast += pstx_status->txbclength;
472 priv->stats.txbytesunicast += pstx_status->txuclength;
474 priv->stats.last_packet_rate = pstx_status->rate;
479 /*-----------------------------------------------------------------------------
480 * Function: cmpk_handle_tx_status()
482 * Overview: Firmware add a new tx feedback status to reduce rx command
483 * packet buffer operation load.
493 * 05/12/2008 amy Create Version 0 porting from windows code.
495 *---------------------------------------------------------------------------*/
496 static void cmpk_handle_tx_status(struct net_device *dev, u8 *pmsg)
498 cmpk_tx_status_t rx_tx_sts;
500 memcpy((void *)&rx_tx_sts, (void *)pmsg, sizeof(cmpk_tx_status_t));
501 /* 2. Use tx feedback info to count TX statistics. */
502 cmpk_count_tx_status(dev, &rx_tx_sts);
507 /*-----------------------------------------------------------------------------
508 * Function: cmpk_handle_tx_rate_history()
510 * Overview: Firmware add a new tx rate history
520 * 05/12/2008 amy Create Version 0 porting from windows code.
522 *---------------------------------------------------------------------------*/
523 static void cmpk_handle_tx_rate_history(struct net_device *dev, u8 *pmsg)
525 cmpk_tx_rahis_t *ptxrate;
527 u16 length = sizeof(cmpk_tx_rahis_t);
529 struct r8192_priv *priv = ieee80211_priv(dev);
533 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
534 (pu1Byte)(&rtState));
536 /* When RF is off, we should not count the packet for hw/sw synchronize
537 reason, ie. there may be a duration while sw switch is changed and
538 hw switch is being changed. */
539 if (rtState == eRfOff)
545 /* Do endian transfer to word alignment(16 bits) for windows system.
546 You must do different endian transfer for linux and MAC OS */
547 for (i = 0; i < (length/4); i++) {
550 temp1 = ptemp[i] & 0x0000FFFF;
551 temp2 = ptemp[i] >> 16;
552 ptemp[i] = (temp1 << 16) | temp2;
555 ptxrate = (cmpk_tx_rahis_t *)pmsg;
560 for (i = 0; i < 16; i++) {
561 /* Collect CCK rate packet num */
563 priv->stats.txrate.cck[i] += ptxrate->cck[i];
565 /* Collect OFDM rate packet num */
567 priv->stats.txrate.ofdm[i] += ptxrate->ofdm[i];
569 for (j = 0; j < 4; j++)
570 priv->stats.txrate.ht_mcs[j][i] += ptxrate->ht_mcs[j][i];
576 /*-----------------------------------------------------------------------------
577 * Function: cmpk_message_handle_rx()
579 * Overview: In the function, we will capture different RX command packet
580 * info. Every RX command packet element has different message
581 * length and meaning in content. We only support three type of RX
582 * command packet now. Please refer to document
583 * ws-06-0063-rtl8190-command-packet-specification.
593 * 05/06/2008 amy Create Version 0 porting from windows code.
595 *---------------------------------------------------------------------------*/
596 extern u32 cmpk_message_handle_rx(struct net_device *dev,
597 struct ieee80211_rx_stats *pstats)
600 u8 cmd_length, exe_cnt = 0;
604 /* 0. Check inpt arguments. If is is a command queue message or
607 return 0; /* This is not a command packet. */
609 /* 1. Read received command packet message length from RFD. */
610 total_length = pstats->Length;
612 /* 2. Read virtual address from RFD. */
613 pcmd_buff = pstats->virtual_address;
615 /* 3. Read command packet element id and length. */
616 element_id = pcmd_buff[0];
618 /* 4. Check every received command packet content according to different
619 element type. Because FW may aggregate RX command packet to
620 minimize transmit time between DRV and FW.*/
621 /* Add a counter to prevent the lock in the loop from being held too
623 while (total_length > 0 && exe_cnt++ < 100) {
624 /* We support aggregation of different cmd in the same packet */
625 element_id = pcmd_buff[0];
627 switch (element_id) {
629 cmpk_handle_tx_feedback(dev, pcmd_buff);
630 cmd_length = CMPK_RX_TX_FB_SIZE;
633 case RX_INTERRUPT_STATUS:
634 cmpk_handle_interrupt_status(dev, pcmd_buff);
635 cmd_length = sizeof(cmpk_intr_sta_t);
638 case BOTH_QUERY_CONFIG:
639 cmpk_handle_query_config_rx(dev, pcmd_buff);
640 cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE;
644 cmpk_handle_tx_status(dev, pcmd_buff);
645 cmd_length = CMPK_RX_TX_STS_SIZE;
648 case RX_TX_PER_PKT_FEEDBACK:
649 /* You must at lease add a switch case element here,
650 Otherwise, we will jump to default case. */
651 cmd_length = CMPK_RX_TX_FB_SIZE;
654 case RX_TX_RATE_HISTORY:
655 cmpk_handle_tx_rate_history(dev, pcmd_buff);
656 cmd_length = CMPK_TX_RAHIS_SIZE;
661 RT_TRACE(COMP_ERR, "---->%s():unknown CMD Element\n",
663 return 1; /* This is a command packet. */
666 total_length -= cmd_length;
667 pcmd_buff += cmd_length;
669 return 1; /* This is a command packet. */