]> Pileus Git - ~andy/linux/blob - drivers/staging/rtl8192e/r819xE_cmdpkt.c
Merge branch 'for-linus' of git://git.open-osd.org/linux-open-osd
[~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 r8192_priv *priv,
37         u8*     code_virtual_address,
38         u32     packettype,
39         u32     buffer_len)
40 {
41         RT_STATUS           rt_status = RT_STATUS_SUCCESS;
42         u16                 frag_threshold;
43         u16                 frag_length = 0, frag_offset = 0;
44         rt_firmware         *pfirmware = priv->pFirmware;
45         struct sk_buff      *skb;
46         unsigned char       *seg_ptr;
47         cb_desc             *tcb_desc;
48         u8                  bLastIniPkt;
49
50         PTX_FWINFO_8190PCI      pTxFwInfo = NULL;
51         int i;
52
53         RT_TRACE(COMP_CMDPKT,"%s(),buffer_len is %d\n",__FUNCTION__,buffer_len);
54         firmware_init_param(priv);
55         //Fragmentation might be required
56         frag_threshold = pfirmware->cmdpacket_frag_thresold;
57         do {
58             if((buffer_len - frag_offset) > frag_threshold) {
59                 frag_length = frag_threshold ;
60                 bLastIniPkt = 0;
61
62             } else {
63                 frag_length =(u16)(buffer_len - frag_offset);
64                 bLastIniPkt = 1;
65
66             }
67
68             /* Allocate skb buffer to contain firmware info and tx descriptor info
69              * add 4 to avoid packet appending overflow.
70              * */
71             skb  = dev_alloc_skb(frag_length + priv->ieee80211->tx_headroom + 4);
72             if(skb == NULL) {
73                 rt_status = RT_STATUS_FAILURE;
74                 goto Failed;
75             }
76
77             tcb_desc = (cb_desc*)(skb->cb + MAX_DEV_ADDR_SIZE);
78             tcb_desc->queue_index = TXCMD_QUEUE;
79             tcb_desc->bCmdOrInit = packettype;
80             tcb_desc->bLastIniPkt = bLastIniPkt;
81             tcb_desc->pkt_size = frag_length;
82
83             //seg_ptr = skb_put(skb, frag_length + priv->ieee80211->tx_headroom);
84             seg_ptr = skb_put(skb, priv->ieee80211->tx_headroom);
85
86             pTxFwInfo = (PTX_FWINFO_8190PCI)seg_ptr;
87             memset(pTxFwInfo,0,sizeof(TX_FWINFO_8190PCI));
88             memset(pTxFwInfo,0x12,8);
89
90             seg_ptr +=sizeof(TX_FWINFO_8190PCI);
91
92             /*
93              * Transform from little endian to big endian
94              * and pending  zero
95              */
96             seg_ptr = skb_tail_pointer(skb);
97             for(i=0 ; i < frag_length; i+=4) {
98                 *seg_ptr++ = ((i+0)<frag_length)?code_virtual_address[i+3]:0;
99                 *seg_ptr++ = ((i+1)<frag_length)?code_virtual_address[i+2]:0;
100                 *seg_ptr++ = ((i+2)<frag_length)?code_virtual_address[i+1]:0;
101                 *seg_ptr++ = ((i+3)<frag_length)?code_virtual_address[i+0]:0;
102             }
103             skb_put(skb, i);
104             priv->ieee80211->softmac_hard_start_xmit(skb, priv->ieee80211);
105
106             code_virtual_address += frag_length;
107             frag_offset += frag_length;
108
109         }while(frag_offset < buffer_len);
110
111 Failed:
112         return rt_status;
113 }
114
115 static void cmpk_count_txstatistic(struct r8192_priv *priv, cmpk_txfb_t *pstx_fb)
116 {
117 #ifdef ENABLE_PS
118         RT_RF_POWER_STATE       rtState;
119
120         pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState));
121
122         // When RF is off, we should not count the packet for hw/sw synchronize
123         // reason, ie. there may be a duration while sw switch is changed and hw
124         // switch is being changed. 2006.12.04, by shien chang.
125         if (rtState == eRfOff)
126         {
127                 return;
128         }
129 #endif
130
131 #ifdef TODO
132         if(pAdapter->bInHctTest)
133                 return;
134 #endif
135         /* We can not know the packet length and transmit type: broadcast or uni
136            or multicast. So the relative statistics must be collected in tx
137            feedback info. */
138         if (pstx_fb->tok)
139         {
140                 priv->stats.txoktotal++;
141
142                 /* We can not make sure broadcast/multicast or unicast mode. */
143                 if (pstx_fb->pkt_type != PACKET_MULTICAST &&
144                     pstx_fb->pkt_type != PACKET_BROADCAST) {
145                         priv->stats.txbytesunicast += pstx_fb->pkt_length;
146                 }
147         }
148 }
149
150
151
152 /*
153  * The function is responsible for extract the message inside TX
154  * feedbck message from firmware. It will contain dedicated info in
155  * ws-06-0063-rtl8190-command-packet-specification. Please
156  * refer to chapter "TX Feedback Element". We have to read 20 bytes
157  * in the command packet.
158  */
159 static void cmpk_handle_tx_feedback(struct r8192_priv *priv, u8 *pmsg)
160 {
161         cmpk_txfb_t             rx_tx_fb;       /* */
162
163         priv->stats.txfeedback++;
164
165         memcpy((u8*)&rx_tx_fb, pmsg, sizeof(cmpk_txfb_t));
166         /* Use tx feedback info to count TX statistics. */
167         cmpk_count_txstatistic(priv, &rx_tx_fb);
168 }
169
170
171 /*
172  * The function is responsible for extract the message from
173  * firmware. It will contain dedicated info in
174  * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc.
175  * Please refer to chapter "Interrupt Status Element".
176  */
177 static void cmpk_handle_interrupt_status(struct r8192_priv *priv, u8 *pmsg)
178 {
179         cmpk_intr_sta_t         rx_intr_status; /* */
180
181         DMESG("---> cmpk_Handle_Interrupt_Status()\n");
182
183         /* 1. Extract TX feedback info from RFD to temp structure buffer. */
184         /* It seems that FW use big endian(MIPS) and DRV use little endian in
185            windows OS. So we have to read the content byte by byte or transfer
186            endian type before copy the message copy. */
187         //rx_bcn_state.Element_ID       = pMsg[0];
188         //rx_bcn_state.Length           = pMsg[1];
189         rx_intr_status.length = pmsg[1];
190         if (rx_intr_status.length != (sizeof(cmpk_intr_sta_t) - 2))
191         {
192                 DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
193                 return;
194         }
195
196
197         // Statistics of beacon for ad-hoc mode.
198         if(     priv->ieee80211->iw_mode == IW_MODE_ADHOC)
199         {
200                 //2 maybe need endian transform?
201                 rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4));
202                 //rx_intr_status.InterruptStatus = N2H4BYTE(*((UINT32 *)(pMsg + 4)));
203
204                 DMESG("interrupt status = 0x%x\n", rx_intr_status.interrupt_status);
205
206                 if (rx_intr_status.interrupt_status & ISR_TxBcnOk)
207                 {
208                         priv->ieee80211->bibsscoordinator = true;
209                         priv->stats.txbeaconokint++;
210                 }
211                 else if (rx_intr_status.interrupt_status & ISR_TxBcnErr)
212                 {
213                         priv->ieee80211->bibsscoordinator = false;
214                         priv->stats.txbeaconerr++;
215                 }
216         }
217
218          // Other informations in interrupt status we need?
219
220
221         DMESG("<---- cmpk_handle_interrupt_status()\n");
222
223 }
224
225
226 /*
227  * The function is responsible for extract the message from
228  * firmware. It will contain dedicated info in
229  * ws-06-0063-rtl8190-command-packet-specification. Please
230  * refer to chapter "Beacon State Element".
231  */
232 static void cmpk_handle_query_config_rx(struct r8192_priv *priv, u8 *pmsg)
233 {
234         cmpk_query_cfg_t        rx_query_cfg;   /* */
235
236         /* 0. Display received message. */
237         //cmpk_Display_Message(CMPK_RX_BEACON_STATE_SIZE, pMsg);
238
239         /* 1. Extract TX feedback info from RFD to temp structure buffer. */
240         /* It seems that FW use big endian(MIPS) and DRV use little endian in
241            windows OS. So we have to read the content byte by byte or transfer
242            endian type before copy the message copy. */
243         //rx_query_cfg.Element_ID       = pMsg[0];
244         //rx_query_cfg.Length           = pMsg[1];
245         rx_query_cfg.cfg_action         = (pmsg[4] & 0x80000000)>>31;
246         rx_query_cfg.cfg_type           = (pmsg[4] & 0x60) >> 5;
247         rx_query_cfg.cfg_size           = (pmsg[4] & 0x18) >> 3;
248         rx_query_cfg.cfg_page           = (pmsg[6] & 0x0F) >> 0;
249         rx_query_cfg.cfg_offset                 = pmsg[7];
250         rx_query_cfg.value                      = (pmsg[8] << 24) | (pmsg[9] << 16) |
251                                                                   (pmsg[10] << 8) | (pmsg[11] << 0);
252         rx_query_cfg.mask                       = (pmsg[12] << 24) | (pmsg[13] << 16) |
253                                                                   (pmsg[14] << 8) | (pmsg[15] << 0);
254
255 }
256
257
258 /*
259  * Count aggregated tx status from firmwar of one type rx command
260  * packet element id = RX_TX_STATUS.
261  */
262 static void cmpk_count_tx_status(struct r8192_priv *priv, cmpk_tx_status_t *pstx_status)
263 {
264
265 #ifdef ENABLE_PS
266
267         RT_RF_POWER_STATE       rtstate;
268
269         pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState));
270
271         // When RF is off, we should not count the packet for hw/sw synchronize
272         // reason, ie. there may be a duration while sw switch is changed and hw
273         // switch is being changed. 2006.12.04, by shien chang.
274         if (rtState == eRfOff)
275         {
276                 return;
277         }
278 #endif
279
280         priv->stats.txfeedbackok        += pstx_status->txok;
281         priv->stats.txoktotal           += pstx_status->txok;
282
283         priv->stats.txbytesunicast              += pstx_status->txuclength;
284 }
285
286
287
288 /*
289  * Firmware add a new tx feedback status to reduce rx command
290  * packet buffer operation load.
291  */
292 static void cmpk_handle_tx_status(struct r8192_priv *priv, u8 *pmsg)
293 {
294         cmpk_tx_status_t        rx_tx_sts;      /* */
295
296         memcpy((void*)&rx_tx_sts, (void*)pmsg, sizeof(cmpk_tx_status_t));
297         /* 2. Use tx feedback info to count TX statistics. */
298         cmpk_count_tx_status(priv, &rx_tx_sts);
299
300 }
301
302
303 /* Firmware add a new tx rate history */
304 static void cmpk_handle_tx_rate_history(struct r8192_priv *priv, u8 *pmsg)
305 {
306         u8                              i;
307         u16                             length = sizeof(cmpk_tx_rahis_t);
308         u32                             *ptemp;
309
310 #ifdef ENABLE_PS
311         pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE, (pu1Byte)(&rtState));
312
313         // When RF is off, we should not count the packet for hw/sw synchronize
314         // reason, ie. there may be a duration while sw switch is changed and hw
315         // switch is being changed. 2006.12.04, by shien chang.
316         if (rtState == eRfOff)
317         {
318                 return;
319         }
320 #endif
321
322         ptemp = (u32 *)pmsg;
323
324         //
325         // Do endian transfer to word alignment(16 bits) for windows system.
326         // You must do different endian transfer for linux and MAC OS
327         //
328         for (i = 0; i < (length/4); i++)
329         {
330                 u16      temp1, temp2;
331
332                 temp1 = ptemp[i]&0x0000FFFF;
333                 temp2 = ptemp[i]>>16;
334                 ptemp[i] = (temp1<<16)|temp2;
335         }
336 }
337
338
339 /*
340  * In the function, we will capture different RX command packet
341  * info. Every RX command packet element has different message
342  * length and meaning in content. We only support three type of RX
343  * command packet now. Please refer to document
344  * ws-06-0063-rtl8190-command-packet-specification.
345  */
346 u32 cmpk_message_handle_rx(struct r8192_priv *priv, struct ieee80211_rx_stats *pstats)
347 {
348 //      u32                     debug_level = DBG_LOUD;
349         int                     total_length;
350         u8                      cmd_length, exe_cnt = 0;
351         u8                      element_id;
352         u8                      *pcmd_buff;
353
354         RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx()\n");
355
356         /* 0. Check inpt arguments. If is is a command queue message or pointer is
357               null. */
358         if (/*(prfd->queue_id != CMPK_RX_QUEUE_ID) || */(pstats== NULL))
359         {
360                 /* Print error message. */
361                 /*RT_TRACE(COMP_SEND, DebugLevel,
362                                 ("\n\r[CMPK]-->Err queue id or pointer"));*/
363                 return 0;       /* This is not a command packet. */
364         }
365
366         /* 1. Read received command packet message length from RFD. */
367         total_length = pstats->Length;
368
369         /* 2. Read virtual address from RFD. */
370         pcmd_buff = pstats->virtual_address;
371
372         /* 3. Read command pakcet element id and length. */
373         element_id = pcmd_buff[0];
374         /*RT_TRACE(COMP_SEND, DebugLevel,
375                         ("\n\r[CMPK]-->element ID=%d Len=%d", element_id, total_length));*/
376
377         /* 4. Check every received command packet conent according to different
378               element type. Because FW may aggregate RX command packet to minimize
379               transmit time between DRV and FW.*/
380         // Add a counter to prevent to locked in the loop too long
381         while (total_length > 0 || exe_cnt++ >100)
382         {
383                 /* 2007/01/17 MH We support aggregation of different cmd in the same packet. */
384                 element_id = pcmd_buff[0];
385
386                 switch(element_id)
387                 {
388                         case RX_TX_FEEDBACK:
389
390                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_FEEDBACK\n");
391                                 cmpk_handle_tx_feedback(priv, pcmd_buff);
392                                 cmd_length = CMPK_RX_TX_FB_SIZE;
393                                 break;
394
395                         case RX_INTERRUPT_STATUS:
396
397                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_INTERRUPT_STATUS\n");
398                                 cmpk_handle_interrupt_status(priv, pcmd_buff);
399                                 cmd_length = sizeof(cmpk_intr_sta_t);
400                                 break;
401
402                         case BOTH_QUERY_CONFIG:
403
404                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():BOTH_QUERY_CONFIG\n");
405                                 cmpk_handle_query_config_rx(priv, pcmd_buff);
406                                 cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE;
407                                 break;
408
409                         case RX_TX_STATUS:
410
411                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_STATUS\n");
412                                 cmpk_handle_tx_status(priv, pcmd_buff);
413                                 cmd_length = CMPK_RX_TX_STS_SIZE;
414                                 break;
415
416                         case RX_TX_PER_PKT_FEEDBACK:
417                                 // You must at lease add a switch case element here,
418                                 // Otherwise, we will jump to default case.
419                                 //DbgPrint("CCX Test\r\n");
420                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_PER_PKT_FEEDBACK\n");
421                                 cmd_length = CMPK_RX_TX_FB_SIZE;
422                                 break;
423
424                         case RX_TX_RATE_HISTORY:
425                                 //DbgPrint(" rx tx rate history\r\n");
426
427                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():RX_TX_HISTORY\n");
428                                 cmpk_handle_tx_rate_history(priv, pcmd_buff);
429                                 cmd_length = CMPK_TX_RAHIS_SIZE;
430                                 break;
431
432                         default:
433
434                                 RT_TRACE(COMP_EVENTS, "---->cmpk_message_handle_rx():unknown CMD Element\n");
435                                 return 1;       /* This is a command packet. */
436                 }
437
438                 total_length -= cmd_length;
439                 pcmd_buff    += cmd_length;
440         }       /* while (total_length > 0) */
441         return  1;      /* This is a command packet. */
442
443         RT_TRACE(COMP_EVENTS, "<----cmpk_message_handle_rx()\n");
444 }