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