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