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
)
87 bool rt_status
= true;
91 struct r8192_priv
*priv
= ieee80211_priv(dev
);
93 u16 frag_length
, frag_offset
= 0;
95 rt_firmware
*pfirmware
= priv
->pFirmware
;
97 unsigned char *seg_ptr
;
101 firmware_init_param(dev
);
102 /* Fragmentation might be required */
103 frag_threshold
= pfirmware
->cmdpacket_frag_thresold
;
105 if ((buffer_len
- frag_offset
) > frag_threshold
) {
106 frag_length
= frag_threshold
;
110 frag_length
= buffer_len
- frag_offset
;
115 /* Allocate skb buffer to contain firmware info and tx
116 descriptor info add 4 to avoid packet appending overflow. */
118 skb
= dev_alloc_skb(USB_HWDESC_HEADER_LEN
+ frag_length
+ 4);
120 skb
= dev_alloc_skb(frag_length
+ 4);
122 memcpy((unsigned char *)(skb
->cb
), &dev
, sizeof(dev
));
123 tcb_desc
= (cb_desc
*)(skb
->cb
+ MAX_DEV_ADDR_SIZE
);
124 tcb_desc
->queue_index
= TXCMD_QUEUE
;
125 tcb_desc
->bCmdOrInit
= packettype
;
126 tcb_desc
->bLastIniPkt
= bLastIniPkt
;
129 skb_reserve(skb
, USB_HWDESC_HEADER_LEN
);
132 seg_ptr
= skb_put(skb
, buffer_len
);
134 * Transform from little endian to big endian
137 memcpy(seg_ptr
, codevirtualaddress
, buffer_len
);
138 tcb_desc
->txbuf_size
= (u16
)buffer_len
;
141 if (!priv
->ieee80211
->check_nic_enough_desc(dev
, tcb_desc
->queue_index
) ||
142 (!skb_queue_empty(&priv
->ieee80211
->skb_waitQ
[tcb_desc
->queue_index
])) ||
143 (priv
->ieee80211
->queue_stop
)) {
144 RT_TRACE(COMP_FIRMWARE
, "======> tx full!\n");
145 skb_queue_tail(&priv
->ieee80211
->skb_waitQ
[tcb_desc
->queue_index
], skb
);
147 priv
->ieee80211
->softmac_hard_start_xmit(skb
, dev
);
150 codevirtualaddress
+= frag_length
;
151 frag_offset
+= frag_length
;
153 } while (frag_offset
< buffer_len
);
161 /*-----------------------------------------------------------------------------
162 * Function: cmpk_counttxstatistic()
166 * Input: PADAPTER pAdapter
167 * CMPK_TXFB_T *psTx_FB
175 * 05/12/2008 amy Create Version 0 porting from windows code.
177 *---------------------------------------------------------------------------*/
178 static void cmpk_count_txstatistic(struct net_device
*dev
, cmpk_txfb_t
*pstx_fb
)
180 struct r8192_priv
*priv
= ieee80211_priv(dev
);
182 RT_RF_POWER_STATE rtState
;
184 pAdapter
->HalFunc
.GetHwRegHandler(pAdapter
, HW_VAR_RF_STATE
,
185 (pu1Byte
)(&rtState
));
187 /* When RF is off, we should not count the packet for hw/sw synchronize
188 reason, ie. there may be a duration while sw switch is changed and
189 hw switch is being changed. */
190 if (rtState
== eRfOff
)
195 if (pAdapter
->bInHctTest
)
198 /* We can not know the packet length and transmit type:
199 broadcast or uni or multicast. So the relative statistics
200 must be collected in tx feedback info. */
202 priv
->stats
.txfeedbackok
++;
203 priv
->stats
.txoktotal
++;
204 priv
->stats
.txokbytestotal
+= pstx_fb
->pkt_length
;
205 priv
->stats
.txokinperiod
++;
207 /* We can not make sure broadcast/multicast or unicast mode. */
208 if (pstx_fb
->pkt_type
== PACKET_MULTICAST
) {
209 priv
->stats
.txmulticast
++;
210 priv
->stats
.txbytesmulticast
+= pstx_fb
->pkt_length
;
211 } else if (pstx_fb
->pkt_type
== PACKET_BROADCAST
) {
212 priv
->stats
.txbroadcast
++;
213 priv
->stats
.txbytesbroadcast
+= pstx_fb
->pkt_length
;
215 priv
->stats
.txunicast
++;
216 priv
->stats
.txbytesunicast
+= pstx_fb
->pkt_length
;
219 priv
->stats
.txfeedbackfail
++;
220 priv
->stats
.txerrtotal
++;
221 priv
->stats
.txerrbytestotal
+= pstx_fb
->pkt_length
;
223 /* We can not make sure broadcast/multicast or unicast mode. */
224 if (pstx_fb
->pkt_type
== PACKET_MULTICAST
)
225 priv
->stats
.txerrmulticast
++;
226 else if (pstx_fb
->pkt_type
== PACKET_BROADCAST
)
227 priv
->stats
.txerrbroadcast
++;
229 priv
->stats
.txerrunicast
++;
232 priv
->stats
.txretrycount
+= pstx_fb
->retry_cnt
;
233 priv
->stats
.txfeedbackretry
+= pstx_fb
->retry_cnt
;
239 /*-----------------------------------------------------------------------------
240 * Function: cmpk_handle_tx_feedback()
242 * Overview: The function is responsible for extract the message inside TX
243 * feedbck message from firmware. It will contain dedicated info in
244 * ws-06-0063-rtl8190-command-packet-specification.
245 * Please refer to chapter "TX Feedback Element".
246 * We have to read 20 bytes in the command packet.
248 * Input: struct net_device *dev
249 * u8 *pmsg - Msg Ptr of the command packet.
257 * 05/08/2008 amy Create Version 0 porting from windows code.
259 *---------------------------------------------------------------------------*/
260 static void cmpk_handle_tx_feedback(struct net_device
*dev
, u8
*pmsg
)
262 struct r8192_priv
*priv
= ieee80211_priv(dev
);
263 cmpk_txfb_t rx_tx_fb
;
265 priv
->stats
.txfeedback
++;
267 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
268 /* It seems that FW use big endian(MIPS) and DRV use little endian in
269 windows OS. So we have to read the content byte by byte or transfer
270 endian type before copy the message copy. */
271 /* Use pointer to transfer structure memory. */
272 memcpy((u8
*)&rx_tx_fb
, pmsg
, sizeof(cmpk_txfb_t
));
273 /* 2. Use tx feedback info to count TX statistics. */
274 cmpk_count_txstatistic(dev
, &rx_tx_fb
);
275 /* Comment previous method for TX statistic function. */
276 /* Collect info TX feedback packet to fill TCB. */
277 /* We can not know the packet length and transmit type: broadcast or uni
282 void cmdpkt_beacontimerinterrupt_819xusb(struct net_device
*dev
)
284 struct r8192_priv
*priv
= ieee80211_priv(dev
);
286 /* 87B have to S/W beacon for DTM encryption_cmn. */
287 if (priv
->ieee80211
->current_network
.mode
== IEEE_A
||
288 priv
->ieee80211
->current_network
.mode
== IEEE_N_5G
||
289 (priv
->ieee80211
->current_network
.mode
== IEEE_N_24G
&&
290 (!priv
->ieee80211
->pHTInfo
->bCurSuppCCK
))) {
292 DMESG("send beacon frame tx rate is 6Mbpm\n");
295 DMESG("send beacon frame tx rate is 1Mbpm\n");
298 rtl819xusb_beacon_tx(dev
, tx_rate
); /* HW Beacon */
306 /*-----------------------------------------------------------------------------
307 * Function: cmpk_handle_interrupt_status()
309 * Overview: The function is responsible for extract the message from
310 * firmware. It will contain dedicated info in
311 * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc.
312 * Please refer to chapter "Interrupt Status Element".
314 * Input: struct net_device *dev
315 * u8 *pmsg - Message Pointer of the command packet.
323 * 05/12/2008 amy Add this for rtl8192 porting from windows code.
325 *---------------------------------------------------------------------------*/
326 static void cmpk_handle_interrupt_status(struct net_device
*dev
, u8
*pmsg
)
328 cmpk_intr_sta_t rx_intr_status
; /* */
329 struct r8192_priv
*priv
= ieee80211_priv(dev
);
331 DMESG("---> cmpk_Handle_Interrupt_Status()\n");
333 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
334 /* It seems that FW use big endian(MIPS) and DRV use little endian in
335 windows OS. So we have to read the content byte by byte or transfer
336 endian type before copy the message copy. */
337 rx_intr_status
.length
= pmsg
[1];
338 if (rx_intr_status
.length
!= (sizeof(cmpk_intr_sta_t
) - 2)) {
339 DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
344 /* Statistics of beacon for ad-hoc mode. */
345 if (priv
->ieee80211
->iw_mode
== IW_MODE_ADHOC
) {
346 /* 2 maybe need endian transform? */
347 rx_intr_status
.interrupt_status
= *((u32
*)(pmsg
+ 4));
349 DMESG("interrupt status = 0x%x\n",
350 rx_intr_status
.interrupt_status
);
352 if (rx_intr_status
.interrupt_status
& ISR_TxBcnOk
) {
353 priv
->ieee80211
->bibsscoordinator
= true;
354 priv
->stats
.txbeaconokint
++;
355 } else if (rx_intr_status
.interrupt_status
& ISR_TxBcnErr
) {
356 priv
->ieee80211
->bibsscoordinator
= false;
357 priv
->stats
.txbeaconerr
++;
360 if (rx_intr_status
.interrupt_status
& ISR_BcnTimerIntr
)
361 cmdpkt_beacontimerinterrupt_819xusb(dev
);
365 /* Other informations in interrupt status we need? */
368 DMESG("<---- cmpk_handle_interrupt_status()\n");
373 /*-----------------------------------------------------------------------------
374 * Function: cmpk_handle_query_config_rx()
376 * Overview: The function is responsible for extract the message from
377 * firmware. It will contain dedicated info in
378 * ws-06-0063-rtl8190-command-packet-specification. Please
379 * refer to chapter "Beacon State Element".
381 * Input: u8 *pmsg - Message Pointer of the command packet.
389 * 05/12/2008 amy Create Version 0 porting from windows code.
391 *---------------------------------------------------------------------------*/
392 static void cmpk_handle_query_config_rx(struct net_device
*dev
, u8
*pmsg
)
394 cmpk_query_cfg_t rx_query_cfg
;
397 /* 1. Extract TX feedback info from RFD to temp structure buffer. */
398 /* It seems that FW use big endian(MIPS) and DRV use little endian in
399 windows OS. So we have to read the content byte by byte or transfer
400 endian type before copy the message copy. */
401 rx_query_cfg
.cfg_action
= (pmsg
[4] & 0x80000000) >> 31;
402 rx_query_cfg
.cfg_type
= (pmsg
[4] & 0x60) >> 5;
403 rx_query_cfg
.cfg_size
= (pmsg
[4] & 0x18) >> 3;
404 rx_query_cfg
.cfg_page
= (pmsg
[6] & 0x0F) >> 0;
405 rx_query_cfg
.cfg_offset
= pmsg
[7];
406 rx_query_cfg
.value
= (pmsg
[8] << 24) | (pmsg
[9] << 16) |
407 (pmsg
[10] << 8) | (pmsg
[11] << 0);
408 rx_query_cfg
.mask
= (pmsg
[12] << 24) | (pmsg
[13] << 16) |
409 (pmsg
[14] << 8) | (pmsg
[15] << 0);
414 /*-----------------------------------------------------------------------------
415 * Function: cmpk_count_tx_status()
417 * Overview: Count aggregated tx status from firmwar of one type rx command
418 * packet element id = RX_TX_STATUS.
428 * 05/12/2008 amy Create Version 0 porting from windows code.
430 *---------------------------------------------------------------------------*/
431 static void cmpk_count_tx_status(struct net_device
*dev
,
432 cmpk_tx_status_t
*pstx_status
)
434 struct r8192_priv
*priv
= ieee80211_priv(dev
);
438 RT_RF_POWER_STATE rtstate
;
440 pAdapter
->HalFunc
.GetHwRegHandler(pAdapter
, HW_VAR_RF_STATE
,
441 (pu1Byte
)(&rtState
));
443 /* When RF is off, we should not count the packet for hw/sw synchronize
444 reason, ie. there may be a duration while sw switch is changed and
445 hw switch is being changed. */
446 if (rtState
== eRfOff
)
450 priv
->stats
.txfeedbackok
+= pstx_status
->txok
;
451 priv
->stats
.txoktotal
+= pstx_status
->txok
;
453 priv
->stats
.txfeedbackfail
+= pstx_status
->txfail
;
454 priv
->stats
.txerrtotal
+= pstx_status
->txfail
;
456 priv
->stats
.txretrycount
+= pstx_status
->txretry
;
457 priv
->stats
.txfeedbackretry
+= pstx_status
->txretry
;
460 priv
->stats
.txmulticast
+= pstx_status
->txmcok
;
461 priv
->stats
.txbroadcast
+= pstx_status
->txbcok
;
462 priv
->stats
.txunicast
+= pstx_status
->txucok
;
464 priv
->stats
.txerrmulticast
+= pstx_status
->txmcfail
;
465 priv
->stats
.txerrbroadcast
+= pstx_status
->txbcfail
;
466 priv
->stats
.txerrunicast
+= pstx_status
->txucfail
;
468 priv
->stats
.txbytesmulticast
+= pstx_status
->txmclength
;
469 priv
->stats
.txbytesbroadcast
+= pstx_status
->txbclength
;
470 priv
->stats
.txbytesunicast
+= pstx_status
->txuclength
;
472 priv
->stats
.last_packet_rate
= pstx_status
->rate
;
477 /*-----------------------------------------------------------------------------
478 * Function: cmpk_handle_tx_status()
480 * Overview: Firmware add a new tx feedback status to reduce rx command
481 * packet buffer operation load.
491 * 05/12/2008 amy Create Version 0 porting from windows code.
493 *---------------------------------------------------------------------------*/
494 static void cmpk_handle_tx_status(struct net_device
*dev
, u8
*pmsg
)
496 cmpk_tx_status_t rx_tx_sts
;
498 memcpy((void *)&rx_tx_sts
, (void *)pmsg
, sizeof(cmpk_tx_status_t
));
499 /* 2. Use tx feedback info to count TX statistics. */
500 cmpk_count_tx_status(dev
, &rx_tx_sts
);
505 /*-----------------------------------------------------------------------------
506 * Function: cmpk_handle_tx_rate_history()
508 * Overview: Firmware add a new tx rate history
518 * 05/12/2008 amy Create Version 0 porting from windows code.
520 *---------------------------------------------------------------------------*/
521 static void cmpk_handle_tx_rate_history(struct net_device
*dev
, u8
*pmsg
)
523 cmpk_tx_rahis_t
*ptxrate
;
525 u16 length
= sizeof(cmpk_tx_rahis_t
);
527 struct r8192_priv
*priv
= ieee80211_priv(dev
);
531 pAdapter
->HalFunc
.GetHwRegHandler(pAdapter
, HW_VAR_RF_STATE
,
532 (pu1Byte
)(&rtState
));
534 /* When RF is off, we should not count the packet for hw/sw synchronize
535 reason, ie. there may be a duration while sw switch is changed and
536 hw switch is being changed. */
537 if (rtState
== eRfOff
)
543 /* Do endian transfer to word alignment(16 bits) for windows system.
544 You must do different endian transfer for linux and MAC OS */
545 for (i
= 0; i
< (length
/4); i
++) {
548 temp1
= ptemp
[i
] & 0x0000FFFF;
549 temp2
= ptemp
[i
] >> 16;
550 ptemp
[i
] = (temp1
<< 16) | temp2
;
553 ptxrate
= (cmpk_tx_rahis_t
*)pmsg
;
558 for (i
= 0; i
< 16; i
++) {
559 /* Collect CCK rate packet num */
561 priv
->stats
.txrate
.cck
[i
] += ptxrate
->cck
[i
];
563 /* Collect OFDM rate packet num */
565 priv
->stats
.txrate
.ofdm
[i
] += ptxrate
->ofdm
[i
];
567 for (j
= 0; j
< 4; j
++)
568 priv
->stats
.txrate
.ht_mcs
[j
][i
] += ptxrate
->ht_mcs
[j
][i
];
574 /*-----------------------------------------------------------------------------
575 * Function: cmpk_message_handle_rx()
577 * Overview: In the function, we will capture different RX command packet
578 * info. Every RX command packet element has different message
579 * length and meaning in content. We only support three type of RX
580 * command packet now. Please refer to document
581 * ws-06-0063-rtl8190-command-packet-specification.
591 * 05/06/2008 amy Create Version 0 porting from windows code.
593 *---------------------------------------------------------------------------*/
594 extern u32
cmpk_message_handle_rx(struct net_device
*dev
,
595 struct ieee80211_rx_stats
*pstats
)
598 u8 cmd_length
, exe_cnt
= 0;
602 /* 0. Check inpt arguments. If is is a command queue message or
605 return 0; /* This is not a command packet. */
607 /* 1. Read received command packet message length from RFD. */
608 total_length
= pstats
->Length
;
610 /* 2. Read virtual address from RFD. */
611 pcmd_buff
= pstats
->virtual_address
;
613 /* 3. Read command packet element id and length. */
614 element_id
= pcmd_buff
[0];
616 /* 4. Check every received command packet content according to different
617 element type. Because FW may aggregate RX command packet to
618 minimize transmit time between DRV and FW.*/
619 /* Add a counter to prevent the lock in the loop from being held too
621 while (total_length
> 0 && exe_cnt
++ < 100) {
622 /* We support aggregation of different cmd in the same packet */
623 element_id
= pcmd_buff
[0];
625 switch (element_id
) {
627 cmpk_handle_tx_feedback(dev
, pcmd_buff
);
628 cmd_length
= CMPK_RX_TX_FB_SIZE
;
631 case RX_INTERRUPT_STATUS
:
632 cmpk_handle_interrupt_status(dev
, pcmd_buff
);
633 cmd_length
= sizeof(cmpk_intr_sta_t
);
636 case BOTH_QUERY_CONFIG
:
637 cmpk_handle_query_config_rx(dev
, pcmd_buff
);
638 cmd_length
= CMPK_BOTH_QUERY_CONFIG_SIZE
;
642 cmpk_handle_tx_status(dev
, pcmd_buff
);
643 cmd_length
= CMPK_RX_TX_STS_SIZE
;
646 case RX_TX_PER_PKT_FEEDBACK
:
647 /* You must at lease add a switch case element here,
648 Otherwise, we will jump to default case. */
649 cmd_length
= CMPK_RX_TX_FB_SIZE
;
652 case RX_TX_RATE_HISTORY
:
653 cmpk_handle_tx_rate_history(dev
, pcmd_buff
);
654 cmd_length
= CMPK_TX_RAHIS_SIZE
;
659 RT_TRACE(COMP_ERR
, "---->%s():unknown CMD Element\n",
661 return 1; /* This is a command packet. */
664 total_length
-= cmd_length
;
665 pcmd_buff
+= cmd_length
;
667 return 1; /* This is a command packet. */