]> Pileus Git - ~andy/linux/blob - drivers/staging/rtl8192e/r819xE_cmdpkt.c
Staging: Merge branch 'staging-next' into 2.6.38-rc3
[~andy/linux] / drivers / staging / rtl8192e / r819xE_cmdpkt.c
1 /******************************************************************************
2
3      (c) Copyright 2008, RealTEK Technologies Inc. All Rights Reserved.
4
5  Module:        r819xusb_cmdpkt.c       (RTL8190 TX/RX command packet handler Source C File)
6
7  Note:      The module is responsible for handling TX and RX command packet.
8                         1. TX : Send set and query configuration command packet.
9                         2. RX : Receive tx feedback, beacon state, query configuration
10                                 command packet.
11
12  Function:
13
14  Export:
15
16  Abbrev:
17
18  History:
19         Data            Who             Remark
20
21         05/06/2008  amy         Create initial version porting from windows driver.
22
23 ******************************************************************************/
24 #include "r8192E.h"
25 #include "r8192E_hw.h"
26 #include "r819xE_cmdpkt.h"
27
28 /*
29  * Driver internal module can call the API to send message to
30  * firmware side. For example, you can send a debug command packet.
31  * Or you can send a request for FW to modify RLX4181 LBUS HW bank.
32  * Otherwise, you can change MAC/PHT/RF register by firmware at
33  * run time. We do not support message more than one segment now.
34  */
35 RT_STATUS cmpk_message_handle_tx(
36         struct net_device *dev,
37         u8*     code_virtual_address,
38         u32     packettype,
39         u32     buffer_len)
40 {
41
42         RT_STATUS           rt_status = RT_STATUS_SUCCESS;
43 #ifdef RTL8192U
44         return rt_status;
45 #else
46         struct r8192_priv   *priv = ieee80211_priv(dev);
47         u16                 frag_threshold;
48         u16                 frag_length = 0, frag_offset = 0;
49         rt_firmware         *pfirmware = priv->pFirmware;
50         struct sk_buff      *skb;
51         unsigned char       *seg_ptr;
52         cb_desc             *tcb_desc;
53         u8                  bLastIniPkt;
54
55         PTX_FWINFO_8190PCI      pTxFwInfo = NULL;
56         int i;
57
58         //spin_lock_irqsave(&priv->tx_lock,flags);
59         RT_TRACE(COMP_CMDPKT,"%s(),buffer_len is %d\n",__FUNCTION__,buffer_len);
60         firmware_init_param(dev);
61         //Fragmentation might be required
62         frag_threshold = pfirmware->cmdpacket_frag_thresold;
63         do {
64             if((buffer_len - frag_offset) > frag_threshold) {
65                 frag_length = frag_threshold ;
66                 bLastIniPkt = 0;
67
68             } else {
69                 frag_length =(u16)(buffer_len - frag_offset);
70                 bLastIniPkt = 1;
71
72             }
73
74             /* Allocate skb buffer to contain firmware info and tx descriptor info
75              * add 4 to avoid packet appending overflow.
76              * */
77 #ifdef RTL8192U
78             skb  = dev_alloc_skb(USB_HWDESC_HEADER_LEN + frag_length + 4);
79 #else
80             skb  = dev_alloc_skb(frag_length + priv->ieee80211->tx_headroom + 4);
81 #endif
82             if(skb == NULL) {
83                 rt_status = RT_STATUS_FAILURE;
84                 goto Failed;
85             }
86
87             memcpy((unsigned char *)(skb->cb),&dev,sizeof(dev));
88             tcb_desc = (cb_desc*)(skb->cb + MAX_DEV_ADDR_SIZE);
89             tcb_desc->queue_index = TXCMD_QUEUE;
90             tcb_desc->bCmdOrInit = packettype;
91             tcb_desc->bLastIniPkt = bLastIniPkt;
92             tcb_desc->pkt_size = frag_length;
93
94 #ifdef RTL8192U
95             skb_reserve(skb, USB_HWDESC_HEADER_LEN);
96 #endif
97
98             //seg_ptr = skb_put(skb, frag_length + priv->ieee80211->tx_headroom);
99             seg_ptr = skb_put(skb, priv->ieee80211->tx_headroom);
100
101             pTxFwInfo = (PTX_FWINFO_8190PCI)seg_ptr;
102             memset(pTxFwInfo,0,sizeof(TX_FWINFO_8190PCI));
103             memset(pTxFwInfo,0x12,8);
104
105             seg_ptr +=sizeof(TX_FWINFO_8190PCI);
106
107             /*
108              * Transform from little endian to big endian
109              * and pending  zero
110              */
111             seg_ptr = skb_tail_pointer(skb);
112             for(i=0 ; i < frag_length; i+=4) {
113                 *seg_ptr++ = ((i+0)<frag_length)?code_virtual_address[i+3]:0;
114                 *seg_ptr++ = ((i+1)<frag_length)?code_virtual_address[i+2]:0;
115                 *seg_ptr++ = ((i+2)<frag_length)?code_virtual_address[i+1]:0;
116                 *seg_ptr++ = ((i+3)<frag_length)?code_virtual_address[i+0]:0;
117             }
118             skb_put(skb, i);
119             priv->ieee80211->softmac_hard_start_xmit(skb,dev);
120
121             code_virtual_address += frag_length;
122             frag_offset += frag_length;
123
124         }while(frag_offset < buffer_len);
125
126 Failed:
127         //spin_unlock_irqrestore(&priv->tx_lock,flags);
128         return rt_status;
129
130
131 #endif
132 }
133
134 static void
135 cmpk_count_txstatistic(
136         struct net_device *dev,
137         cmpk_txfb_t     *pstx_fb)
138 {
139         struct r8192_priv *priv = ieee80211_priv(dev);
140 #ifdef ENABLE_PS
141         RT_RF_POWER_STATE       rtState;
142
143         pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState));
144
145         // When RF is off, we should not count the packet for hw/sw synchronize
146         // reason, ie. there may be a duration while sw switch is changed and hw
147         // switch is being changed. 2006.12.04, by shien chang.
148         if (rtState == eRfOff)
149         {
150                 return;
151         }
152 #endif
153
154 #ifdef TODO
155         if(pAdapter->bInHctTest)
156                 return;
157 #endif
158         /* We can not know the packet length and transmit type: broadcast or uni
159            or multicast. So the relative statistics must be collected in tx
160            feedback info. */
161         if (pstx_fb->tok)
162         {
163                 priv->stats.txoktotal++;
164
165                 /* We can not make sure broadcast/multicast or unicast mode. */
166                 if (pstx_fb->pkt_type != PACKET_MULTICAST &&
167                     pstx_fb->pkt_type != PACKET_BROADCAST) {
168                         priv->stats.txbytesunicast += pstx_fb->pkt_length;
169                 }
170         }
171 }
172
173
174
175 /*
176  * The function is responsible for extract the message inside TX
177  * feedbck message from firmware. It will contain dedicated info in
178  * ws-06-0063-rtl8190-command-packet-specification. Please
179  * refer to chapter "TX Feedback Element". We have to read 20 bytes
180  * in the command packet.
181  */
182 static void
183 cmpk_handle_tx_feedback(
184         struct net_device *dev,
185         u8      *       pmsg)
186 {
187         struct r8192_priv *priv = ieee80211_priv(dev);
188         cmpk_txfb_t             rx_tx_fb;       /* */
189
190         priv->stats.txfeedback++;
191
192         /* 0. Display received message. */
193         //cmpk_Display_Message(CMPK_RX_TX_FB_SIZE, pMsg);
194
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 #if 0           // The TX FEEDBACK packet element address
200         //rx_tx_fb.Element_ID   = pMsg[0];
201         //rx_tx_fb.Length               = pMsg[1];
202         rx_tx_fb.TOK                    = pMsg[2]>>7;
203         rx_tx_fb.Fail_Reason    = (pMsg[2] & 0x70) >> 4;
204         rx_tx_fb.TID                    = (pMsg[2] & 0x0F);
205         rx_tx_fb.Qos_Pkt                = pMsg[3] >> 7;
206         rx_tx_fb.Bandwidth              = (pMsg[3] & 0x40) >> 6;
207         rx_tx_fb.Retry_Cnt              = pMsg[5];
208         rx_tx_fb.Pkt_ID                 = (pMsg[6] << 8) | pMsg[7];
209         rx_tx_fb.Seq_Num                = (pMsg[8] << 8) | pMsg[9];
210         rx_tx_fb.S_Rate                 = pMsg[10];
211         rx_tx_fb.F_Rate                 = pMsg[11];
212         rx_tx_fb.S_RTS_Rate     = pMsg[12];
213         rx_tx_fb.F_RTS_Rate     = pMsg[13];
214         rx_tx_fb.pkt_length     = (pMsg[14] << 8) | pMsg[15];
215 #endif
216         /* 2007/07/05 MH Use pointer to transfer structure memory. */
217         //memcpy((UINT8 *)&rx_tx_fb, pMsg, sizeof(CMPK_TXFB_T));
218         memcpy((u8*)&rx_tx_fb, pmsg, sizeof(cmpk_txfb_t));
219         /* 2. Use tx feedback info to count TX statistics. */
220         cmpk_count_txstatistic(dev, &rx_tx_fb);
221 #if 0
222         /* 2007/07/11 MH Assign current operate rate.  */
223         if (pAdapter->RegWirelessMode == WIRELESS_MODE_A ||
224                 pAdapter->RegWirelessMode == WIRELESS_MODE_B ||
225                 pAdapter->RegWirelessMode == WIRELESS_MODE_G)
226         {
227                 pMgntInfo->CurrentOperaRate = (rx_tx_fb.F_Rate & 0x7F);
228         }
229         else if (pAdapter->RegWirelessMode == WIRELESS_MODE_N_24G ||
230                          pAdapter->RegWirelessMode == WIRELESS_MODE_N_5G)
231         {
232                 pMgntInfo->HTCurrentOperaRate = (rx_tx_fb.F_Rate & 0x8F);
233         }
234 #endif
235         /* 2007/01/17 MH Comment previous method for TX statistic function. */
236         /* Collect info TX feedback packet to fill TCB. */
237         /* We can not know the packet length and transmit type: broadcast or uni
238            or multicast. */
239         //CountTxStatistics( pAdapter, &tcb );
240
241 }
242
243
244 /*
245  * The function is responsible for extract the message from
246  * firmware. It will contain dedicated info in
247  * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc.
248  * Please refer to chapter "Interrupt Status Element".
249  */
250 static  void
251 cmpk_handle_interrupt_status(
252         struct net_device *dev,
253         u8*     pmsg)
254 {
255         cmpk_intr_sta_t         rx_intr_status; /* */
256         struct r8192_priv *priv = ieee80211_priv(dev);
257
258         DMESG("---> cmpk_Handle_Interrupt_Status()\n");
259
260         /* 0. Display received message. */
261         //cmpk_Display_Message(CMPK_RX_BEACON_STATE_SIZE, pMsg);
262
263         /* 1. Extract TX feedback info from RFD to temp structure buffer. */
264         /* It seems that FW use big endian(MIPS) and DRV use little endian in
265            windows OS. So we have to read the content byte by byte or transfer
266            endian type before copy the message copy. */
267         //rx_bcn_state.Element_ID       = pMsg[0];
268         //rx_bcn_state.Length           = pMsg[1];
269         rx_intr_status.length = pmsg[1];
270         if (rx_intr_status.length != (sizeof(cmpk_intr_sta_t) - 2))
271         {
272                 DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
273                 return;
274         }
275
276
277         // Statistics of beacon for ad-hoc mode.
278         if(     priv->ieee80211->iw_mode == IW_MODE_ADHOC)
279         {
280                 //2 maybe need endian transform?
281                 rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4));
282                 //rx_intr_status.InterruptStatus = N2H4BYTE(*((UINT32 *)(pMsg + 4)));
283
284                 DMESG("interrupt status = 0x%x\n", rx_intr_status.interrupt_status);
285
286                 if (rx_intr_status.interrupt_status & ISR_TxBcnOk)
287                 {
288                         priv->ieee80211->bibsscoordinator = true;
289                         priv->stats.txbeaconokint++;
290                 }
291                 else if (rx_intr_status.interrupt_status & ISR_TxBcnErr)
292                 {
293                         priv->ieee80211->bibsscoordinator = false;
294                         priv->stats.txbeaconerr++;
295                 }
296         }
297
298          // Other informations in interrupt status we need?
299
300
301         DMESG("<---- cmpk_handle_interrupt_status()\n");
302
303 }
304
305
306 /*
307  * The function is responsible for extract the message from
308  * firmware. It will contain dedicated info in
309  * ws-06-0063-rtl8190-command-packet-specification. Please
310  * refer to chapter "Beacon State Element".
311  */
312 static  void
313 cmpk_handle_query_config_rx(
314         struct net_device *dev,
315         u8*        pmsg)
316 {
317         cmpk_query_cfg_t        rx_query_cfg;   /* */
318
319         /* 0. Display received message. */
320         //cmpk_Display_Message(CMPK_RX_BEACON_STATE_SIZE, pMsg);
321
322         /* 1. Extract TX feedback info from RFD to temp structure buffer. */
323         /* It seems that FW use big endian(MIPS) and DRV use little endian in
324            windows OS. So we have to read the content byte by byte or transfer
325            endian type before copy the message copy. */
326         //rx_query_cfg.Element_ID       = pMsg[0];
327         //rx_query_cfg.Length           = pMsg[1];
328         rx_query_cfg.cfg_action         = (pmsg[4] & 0x80000000)>>31;
329         rx_query_cfg.cfg_type           = (pmsg[4] & 0x60) >> 5;
330         rx_query_cfg.cfg_size           = (pmsg[4] & 0x18) >> 3;
331         rx_query_cfg.cfg_page           = (pmsg[6] & 0x0F) >> 0;
332         rx_query_cfg.cfg_offset                 = pmsg[7];
333         rx_query_cfg.value                      = (pmsg[8] << 24) | (pmsg[9] << 16) |
334                                                                   (pmsg[10] << 8) | (pmsg[11] << 0);
335         rx_query_cfg.mask                       = (pmsg[12] << 24) | (pmsg[13] << 16) |
336                                                                   (pmsg[14] << 8) | (pmsg[15] << 0);
337
338 }
339
340
341 /*
342  * Count aggregated tx status from firmwar of one type rx command
343  * packet element id = RX_TX_STATUS.
344  */
345 static  void    cmpk_count_tx_status(   struct net_device *dev,
346                                                                         cmpk_tx_status_t        *pstx_status)
347 {
348         struct r8192_priv *priv = ieee80211_priv(dev);
349
350 #ifdef ENABLE_PS
351
352         RT_RF_POWER_STATE       rtstate;
353
354         pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState));
355
356         // When RF is off, we should not count the packet for hw/sw synchronize
357         // reason, ie. there may be a duration while sw switch is changed and hw
358         // switch is being changed. 2006.12.04, by shien chang.
359         if (rtState == eRfOff)
360         {
361                 return;
362         }
363 #endif
364
365         priv->stats.txfeedbackok        += pstx_status->txok;
366         priv->stats.txoktotal           += pstx_status->txok;
367
368         priv->stats.txbytesunicast              += pstx_status->txuclength;
369 }
370
371
372
373 /*
374  * Firmware add a new tx feedback status to reduce rx command
375  * packet buffer operation load.
376  */
377 static  void
378 cmpk_handle_tx_status(
379         struct net_device *dev,
380         u8*        pmsg)
381 {
382         cmpk_tx_status_t        rx_tx_sts;      /* */
383
384         memcpy((void*)&rx_tx_sts, (void*)pmsg, sizeof(cmpk_tx_status_t));
385         /* 2. Use tx feedback info to count TX statistics. */
386         cmpk_count_tx_status(dev, &rx_tx_sts);
387
388 }
389
390
391 /* Firmware add a new tx rate history */
392 static  void
393 cmpk_handle_tx_rate_history(
394         struct net_device *dev,
395         u8*        pmsg)
396 {
397         u8                              i;
398         u16                             length = sizeof(cmpk_tx_rahis_t);
399         u32                             *ptemp;
400
401 #ifdef ENABLE_PS
402         pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState));
403
404         // When RF is off, we should not count the packet for hw/sw synchronize
405         // reason, ie. there may be a duration while sw switch is changed and hw
406         // switch is being changed. 2006.12.04, by shien chang.
407         if (rtState == eRfOff)
408         {
409                 return;
410         }
411 #endif
412
413         ptemp = (u32 *)pmsg;
414
415         //
416         // Do endian transfer to word alignment(16 bits) for windows system.
417         // You must do different endian transfer for linux and MAC OS
418         //
419         for (i = 0; i < (length/4); i++)
420         {
421                 u16      temp1, temp2;
422
423                 temp1 = ptemp[i]&0x0000FFFF;
424                 temp2 = ptemp[i]>>16;
425                 ptemp[i] = (temp1<<16)|temp2;
426         }
427 }
428
429
430 /*
431  * In the function, we will capture different RX command packet
432  * info. Every RX command packet element has different message
433  * length and meaning in content. We only support three type of RX
434  * command packet now. Please refer to document
435  * ws-06-0063-rtl8190-command-packet-specification.
436  */
437 u32 cmpk_message_handle_rx(struct net_device *dev, struct ieee80211_rx_stats *pstats)
438 {
439 //      u32                     debug_level = DBG_LOUD;
440         int                     total_length;
441         u8                      cmd_length, exe_cnt = 0;
442         u8                      element_id;
443         u8                      *pcmd_buff;
444
445         RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx()\n");
446
447         /* 0. Check inpt arguments. If is is a command queue message or pointer is
448               null. */
449         if (/*(prfd->queue_id != CMPK_RX_QUEUE_ID) || */(pstats== NULL))
450         {
451                 /* Print error message. */
452                 /*RT_TRACE(COMP_SEND, DebugLevel,
453                                 ("\n\r[CMPK]-->Err queue id or pointer"));*/
454                 return 0;       /* This is not a command packet. */
455         }
456
457         /* 1. Read received command packet message length from RFD. */
458         total_length = pstats->Length;
459
460         /* 2. Read virtual address from RFD. */
461         pcmd_buff = pstats->virtual_address;
462
463         /* 3. Read command pakcet element id and length. */
464         element_id = pcmd_buff[0];
465         /*RT_TRACE(COMP_SEND, DebugLevel,
466                         ("\n\r[CMPK]-->element ID=%d Len=%d", element_id, total_length));*/
467
468         /* 4. Check every received command packet conent according to different
469               element type. Because FW may aggregate RX command packet to minimize
470               transmit time between DRV and FW.*/
471         // Add a counter to prevent to locked in the loop too long
472         while (total_length > 0 || exe_cnt++ >100)
473         {
474                 /* 2007/01/17 MH We support aggregation of different cmd in the same packet. */
475                 element_id = pcmd_buff[0];
476
477                 switch(element_id)
478                 {
479                         case RX_TX_FEEDBACK:
480
481                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_FEEDBACK\n");
482                                 cmpk_handle_tx_feedback (dev, pcmd_buff);
483                                 cmd_length = CMPK_RX_TX_FB_SIZE;
484                                 break;
485
486                         case RX_INTERRUPT_STATUS:
487
488                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_INTERRUPT_STATUS\n");
489                                 cmpk_handle_interrupt_status(dev, pcmd_buff);
490                                 cmd_length = sizeof(cmpk_intr_sta_t);
491                                 break;
492
493                         case BOTH_QUERY_CONFIG:
494
495                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():BOTH_QUERY_CONFIG\n");
496                                 cmpk_handle_query_config_rx(dev, pcmd_buff);
497                                 cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE;
498                                 break;
499
500                         case RX_TX_STATUS:
501
502                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_STATUS\n");
503                                 cmpk_handle_tx_status(dev, pcmd_buff);
504                                 cmd_length = CMPK_RX_TX_STS_SIZE;
505                                 break;
506
507                         case RX_TX_PER_PKT_FEEDBACK:
508                                 // You must at lease add a switch case element here,
509                                 // Otherwise, we will jump to default case.
510                                 //DbgPrint("CCX Test\r\n");
511                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_PER_PKT_FEEDBACK\n");
512                                 cmd_length = CMPK_RX_TX_FB_SIZE;
513                                 break;
514
515                         case RX_TX_RATE_HISTORY:
516                                 //DbgPrint(" rx tx rate history\r\n");
517
518                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_HISTORY\n");
519                                 cmpk_handle_tx_rate_history(dev, pcmd_buff);
520                                 cmd_length = CMPK_TX_RAHIS_SIZE;
521                                 break;
522
523                         default:
524
525                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():unknown CMD Element\n");
526                                 return 1;       /* This is a command packet. */
527                 }
528
529                 total_length -= cmd_length;
530                 pcmd_buff    += cmd_length;
531         }       /* while (total_length > 0) */
532         return  1;      /* This is a command packet. */
533
534         RT_TRACE(COMP_EVENTS, "<----cmpk_message_handle_rx()\n");
535 }