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);
40 memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
41 tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
42 tcb_desc->queue_index = TXCMD_QUEUE;
43 tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_NORMAL;
44 tcb_desc->bLastIniPkt = 0;
45 skb_reserve(skb, USB_HWDESC_HEADER_LEN);
46 ptr_buf = skb_put(skb, DataLen);
47 memcpy(ptr_buf, pData, DataLen);
48 tcb_desc->txbuf_size = (u16)DataLen;
50 if (!priv->ieee80211->check_nic_enough_desc(dev, tcb_desc->queue_index) ||
51 (!skb_queue_empty(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index])) ||
52 (priv->ieee80211->queue_stop)) {
53 RT_TRACE(COMP_FIRMWARE, "=== NULL packet ======> tx full!\n");
54 skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb);
56 priv->ieee80211->softmac_hard_start_xmit(skb, dev);
62 /*-----------------------------------------------------------------------------
63 * Function: cmpk_message_handle_tx()
65 * Overview: Driver internal module can call the API to send message to
66 * firmware side. For example, you can send a debug command packet.
67 * Or you can send a request for FW to modify RLX4181 LBUS HW bank.
68 * Otherwise, you can change MAC/PHT/RF register by firmware at
69 * run time. We do not support message more than one segment now.
79 * 05/06/2008 amy porting from windows code.
81 *---------------------------------------------------------------------------*/
82 extern rt_status cmpk_message_handle_tx(struct net_device *dev,
83 u8 *codevirtualaddress,
84 u32 packettype, u32 buffer_len)
89 /*-----------------------------------------------------------------------------
90 * Function: cmpk_counttxstatistic()
94 * Input: PADAPTER pAdapter
95 * CMPK_TXFB_T *psTx_FB
103 * 05/12/2008 amy Create Version 0 porting from windows code.
105 *---------------------------------------------------------------------------*/
106 static void cmpk_count_txstatistic(struct net_device *dev, cmpk_txfb_t *pstx_fb)
108 struct r8192_priv *priv = ieee80211_priv(dev);
110 RT_RF_POWER_STATE rtState;
112 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
113 (pu1Byte)(&rtState));
115 /* When RF is off, we should not count the packet for hw/sw synchronize
116 reason, ie. there may be a duration while sw switch is changed and
117 hw switch is being changed. */
118 if (rtState == eRfOff)
123 if (pAdapter->bInHctTest)
126 /* We can not know the packet length and transmit type:
127 broadcast or uni or multicast. So the relative statistics
128 must be collected in tx feedback info. */
130 priv->stats.txfeedbackok++;
131 priv->stats.txoktotal++;
132 priv->stats.txokbytestotal += pstx_fb->pkt_length;
133 priv->stats.txokinperiod++;
135 /* We can not make sure broadcast/multicast or unicast mode. */
136 if (pstx_fb->pkt_type == PACKET_MULTICAST) {
137 priv->stats.txmulticast++;
138 priv->stats.txbytesmulticast += pstx_fb->pkt_length;
139 } else if (pstx_fb->pkt_type == PACKET_BROADCAST) {
140 priv->stats.txbroadcast++;
141 priv->stats.txbytesbroadcast += pstx_fb->pkt_length;
143 priv->stats.txunicast++;
144 priv->stats.txbytesunicast += pstx_fb->pkt_length;
147 priv->stats.txfeedbackfail++;
148 priv->stats.txerrtotal++;
149 priv->stats.txerrbytestotal += pstx_fb->pkt_length;
151 /* We can not make sure broadcast/multicast or unicast mode. */
152 if (pstx_fb->pkt_type == PACKET_MULTICAST)
153 priv->stats.txerrmulticast++;
154 else if (pstx_fb->pkt_type == PACKET_BROADCAST)
155 priv->stats.txerrbroadcast++;
157 priv->stats.txerrunicast++;
160 priv->stats.txretrycount += pstx_fb->retry_cnt;
161 priv->stats.txfeedbackretry += pstx_fb->retry_cnt;
167 /*-----------------------------------------------------------------------------
168 * Function: cmpk_handle_tx_feedback()
170 * Overview: The function is responsible for extract the message inside TX
171 * feedbck message from firmware. It will contain dedicated info in
172 * ws-06-0063-rtl8190-command-packet-specification.
173 * Please refer to chapter "TX Feedback Element".
174 * We have to read 20 bytes in the command packet.
176 * Input: struct net_device *dev
177 * u8 *pmsg - Msg Ptr of the command packet.
185 * 05/08/2008 amy Create Version 0 porting from windows code.
187 *---------------------------------------------------------------------------*/
188 static void cmpk_handle_tx_feedback(struct net_device *dev, u8 *pmsg)
190 struct r8192_priv *priv = ieee80211_priv(dev);
191 cmpk_txfb_t rx_tx_fb;
193 priv->stats.txfeedback++;
195 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
196 /* It seems that FW use big endian(MIPS) and DRV use little endian in
197 windows OS. So we have to read the content byte by byte or transfer
198 endian type before copy the message copy. */
199 /* Use pointer to transfer structure memory. */
200 memcpy((u8 *)&rx_tx_fb, pmsg, sizeof(cmpk_txfb_t));
201 /* 2. Use tx feedback info to count TX statistics. */
202 cmpk_count_txstatistic(dev, &rx_tx_fb);
203 /* Comment previous method for TX statistic function. */
204 /* Collect info TX feedback packet to fill TCB. */
205 /* We can not know the packet length and transmit type: broadcast or uni
210 void cmdpkt_beacontimerinterrupt_819xusb(struct net_device *dev)
212 struct r8192_priv *priv = ieee80211_priv(dev);
214 /* 87B have to S/W beacon for DTM encryption_cmn. */
215 if (priv->ieee80211->current_network.mode == IEEE_A ||
216 priv->ieee80211->current_network.mode == IEEE_N_5G ||
217 (priv->ieee80211->current_network.mode == IEEE_N_24G &&
218 (!priv->ieee80211->pHTInfo->bCurSuppCCK))) {
220 DMESG("send beacon frame tx rate is 6Mbpm\n");
223 DMESG("send beacon frame tx rate is 1Mbpm\n");
226 rtl819xusb_beacon_tx(dev, tx_rate); /* HW Beacon */
234 /*-----------------------------------------------------------------------------
235 * Function: cmpk_handle_interrupt_status()
237 * Overview: The function is responsible for extract the message from
238 * firmware. It will contain dedicated info in
239 * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc.
240 * Please refer to chapter "Interrupt Status Element".
242 * Input: struct net_device *dev
243 * u8 *pmsg - Message Pointer of the command packet.
251 * 05/12/2008 amy Add this for rtl8192 porting from windows code.
253 *---------------------------------------------------------------------------*/
254 static void cmpk_handle_interrupt_status(struct net_device *dev, u8 *pmsg)
256 cmpk_intr_sta_t rx_intr_status; /* */
257 struct r8192_priv *priv = ieee80211_priv(dev);
259 DMESG("---> cmpk_Handle_Interrupt_Status()\n");
261 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
262 /* It seems that FW use big endian(MIPS) and DRV use little endian in
263 windows OS. So we have to read the content byte by byte or transfer
264 endian type before copy the message copy. */
265 rx_intr_status.length = pmsg[1];
266 if (rx_intr_status.length != (sizeof(cmpk_intr_sta_t) - 2)) {
267 DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
272 /* Statistics of beacon for ad-hoc mode. */
273 if (priv->ieee80211->iw_mode == IW_MODE_ADHOC) {
274 /* 2 maybe need endian transform? */
275 rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4));
277 DMESG("interrupt status = 0x%x\n",
278 rx_intr_status.interrupt_status);
280 if (rx_intr_status.interrupt_status & ISR_TxBcnOk) {
281 priv->ieee80211->bibsscoordinator = true;
282 priv->stats.txbeaconokint++;
283 } else if (rx_intr_status.interrupt_status & ISR_TxBcnErr) {
284 priv->ieee80211->bibsscoordinator = false;
285 priv->stats.txbeaconerr++;
288 if (rx_intr_status.interrupt_status & ISR_BcnTimerIntr)
289 cmdpkt_beacontimerinterrupt_819xusb(dev);
293 /* Other informations in interrupt status we need? */
296 DMESG("<---- cmpk_handle_interrupt_status()\n");
301 /*-----------------------------------------------------------------------------
302 * Function: cmpk_handle_query_config_rx()
304 * Overview: The function is responsible for extract the message from
305 * firmware. It will contain dedicated info in
306 * ws-06-0063-rtl8190-command-packet-specification. Please
307 * refer to chapter "Beacon State Element".
309 * Input: u8 *pmsg - Message Pointer of the command packet.
317 * 05/12/2008 amy Create Version 0 porting from windows code.
319 *---------------------------------------------------------------------------*/
320 static void cmpk_handle_query_config_rx(struct net_device *dev, u8 *pmsg)
322 cmpk_query_cfg_t rx_query_cfg;
325 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
326 /* It seems that FW use big endian(MIPS) and DRV use little endian in
327 windows OS. So we have to read the content byte by byte or transfer
328 endian type before copy the message copy. */
329 rx_query_cfg.cfg_action = (pmsg[4] & 0x80000000) >> 31;
330 rx_query_cfg.cfg_type = (pmsg[4] & 0x60) >> 5;
331 rx_query_cfg.cfg_size = (pmsg[4] & 0x18) >> 3;
332 rx_query_cfg.cfg_page = (pmsg[6] & 0x0F) >> 0;
333 rx_query_cfg.cfg_offset = pmsg[7];
334 rx_query_cfg.value = (pmsg[8] << 24) | (pmsg[9] << 16) |
335 (pmsg[10] << 8) | (pmsg[11] << 0);
336 rx_query_cfg.mask = (pmsg[12] << 24) | (pmsg[13] << 16) |
337 (pmsg[14] << 8) | (pmsg[15] << 0);
342 /*-----------------------------------------------------------------------------
343 * Function: cmpk_count_tx_status()
345 * Overview: Count aggregated tx status from firmwar of one type rx command
346 * packet element id = RX_TX_STATUS.
356 * 05/12/2008 amy Create Version 0 porting from windows code.
358 *---------------------------------------------------------------------------*/
359 static void cmpk_count_tx_status(struct net_device *dev,
360 cmpk_tx_status_t *pstx_status)
362 struct r8192_priv *priv = ieee80211_priv(dev);
366 RT_RF_POWER_STATE rtstate;
368 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
369 (pu1Byte)(&rtState));
371 /* When RF is off, we should not count the packet for hw/sw synchronize
372 reason, ie. there may be a duration while sw switch is changed and
373 hw switch is being changed. */
374 if (rtState == eRfOff)
378 priv->stats.txfeedbackok += pstx_status->txok;
379 priv->stats.txoktotal += pstx_status->txok;
381 priv->stats.txfeedbackfail += pstx_status->txfail;
382 priv->stats.txerrtotal += pstx_status->txfail;
384 priv->stats.txretrycount += pstx_status->txretry;
385 priv->stats.txfeedbackretry += pstx_status->txretry;
388 priv->stats.txmulticast += pstx_status->txmcok;
389 priv->stats.txbroadcast += pstx_status->txbcok;
390 priv->stats.txunicast += pstx_status->txucok;
392 priv->stats.txerrmulticast += pstx_status->txmcfail;
393 priv->stats.txerrbroadcast += pstx_status->txbcfail;
394 priv->stats.txerrunicast += pstx_status->txucfail;
396 priv->stats.txbytesmulticast += pstx_status->txmclength;
397 priv->stats.txbytesbroadcast += pstx_status->txbclength;
398 priv->stats.txbytesunicast += pstx_status->txuclength;
400 priv->stats.last_packet_rate = pstx_status->rate;
405 /*-----------------------------------------------------------------------------
406 * Function: cmpk_handle_tx_status()
408 * Overview: Firmware add a new tx feedback status to reduce rx command
409 * packet buffer operation load.
419 * 05/12/2008 amy Create Version 0 porting from windows code.
421 *---------------------------------------------------------------------------*/
422 static void cmpk_handle_tx_status(struct net_device *dev, u8 *pmsg)
424 cmpk_tx_status_t rx_tx_sts;
426 memcpy((void *)&rx_tx_sts, (void *)pmsg, sizeof(cmpk_tx_status_t));
427 /* 2. Use tx feedback info to count TX statistics. */
428 cmpk_count_tx_status(dev, &rx_tx_sts);
433 /*-----------------------------------------------------------------------------
434 * Function: cmpk_handle_tx_rate_history()
436 * Overview: Firmware add a new tx rate history
446 * 05/12/2008 amy Create Version 0 porting from windows code.
448 *---------------------------------------------------------------------------*/
449 static void cmpk_handle_tx_rate_history(struct net_device *dev, u8 *pmsg)
451 cmpk_tx_rahis_t *ptxrate;
453 u16 length = sizeof(cmpk_tx_rahis_t);
455 struct r8192_priv *priv = ieee80211_priv(dev);
459 pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
460 (pu1Byte)(&rtState));
462 /* When RF is off, we should not count the packet for hw/sw synchronize
463 reason, ie. there may be a duration while sw switch is changed and
464 hw switch is being changed. */
465 if (rtState == eRfOff)
471 /* Do endian transfer to word alignment(16 bits) for windows system.
472 You must do different endian transfer for linux and MAC OS */
473 for (i = 0; i < (length/4); i++) {
476 temp1 = ptemp[i] & 0x0000FFFF;
477 temp2 = ptemp[i] >> 16;
478 ptemp[i] = (temp1 << 16) | temp2;
481 ptxrate = (cmpk_tx_rahis_t *)pmsg;
486 for (i = 0; i < 16; i++) {
487 /* Collect CCK rate packet num */
489 priv->stats.txrate.cck[i] += ptxrate->cck[i];
491 /* Collect OFDM rate packet num */
493 priv->stats.txrate.ofdm[i] += ptxrate->ofdm[i];
495 for (j = 0; j < 4; j++)
496 priv->stats.txrate.ht_mcs[j][i] += ptxrate->ht_mcs[j][i];
502 /*-----------------------------------------------------------------------------
503 * Function: cmpk_message_handle_rx()
505 * Overview: In the function, we will capture different RX command packet
506 * info. Every RX command packet element has different message
507 * length and meaning in content. We only support three type of RX
508 * command packet now. Please refer to document
509 * ws-06-0063-rtl8190-command-packet-specification.
519 * 05/06/2008 amy Create Version 0 porting from windows code.
521 *---------------------------------------------------------------------------*/
522 extern u32 cmpk_message_handle_rx(struct net_device *dev,
523 struct ieee80211_rx_stats *pstats)
526 u8 cmd_length, exe_cnt = 0;
530 /* 0. Check inpt arguments. If is is a command queue message or
533 return 0; /* This is not a command packet. */
535 /* 1. Read received command packet message length from RFD. */
536 total_length = pstats->Length;
538 /* 2. Read virtual address from RFD. */
539 pcmd_buff = pstats->virtual_address;
541 /* 3. Read command packet element id and length. */
542 element_id = pcmd_buff[0];
544 /* 4. Check every received command packet content according to different
545 element type. Because FW may aggregate RX command packet to
546 minimize transmit time between DRV and FW.*/
547 /* Add a counter to prevent the lock in the loop from being held too
549 while (total_length > 0 && exe_cnt++ < 100) {
550 /* We support aggregation of different cmd in the same packet */
551 element_id = pcmd_buff[0];
553 switch (element_id) {
555 cmpk_handle_tx_feedback(dev, pcmd_buff);
556 cmd_length = CMPK_RX_TX_FB_SIZE;
559 case RX_INTERRUPT_STATUS:
560 cmpk_handle_interrupt_status(dev, pcmd_buff);
561 cmd_length = sizeof(cmpk_intr_sta_t);
564 case BOTH_QUERY_CONFIG:
565 cmpk_handle_query_config_rx(dev, pcmd_buff);
566 cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE;
570 cmpk_handle_tx_status(dev, pcmd_buff);
571 cmd_length = CMPK_RX_TX_STS_SIZE;
574 case RX_TX_PER_PKT_FEEDBACK:
575 /* You must at lease add a switch case element here,
576 Otherwise, we will jump to default case. */
577 cmd_length = CMPK_RX_TX_FB_SIZE;
580 case RX_TX_RATE_HISTORY:
581 cmpk_handle_tx_rate_history(dev, pcmd_buff);
582 cmd_length = CMPK_TX_RAHIS_SIZE;
587 RT_TRACE(COMP_ERR, "---->%s():unknown CMD Element\n",
589 return 1; /* This is a command packet. */
592 total_length -= cmd_length;
593 pcmd_buff += cmd_length;
595 return 1; /* This is a command packet. */