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