linux/drivers/staging/rtl8192e/r819xE_cmdpkt.c
<<
>>
Prefs
   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 */
  35RT_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
 111Failed:
 112        return rt_status;
 113}
 114
 115static 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 */
 159static 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 */
 177static 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 */
 232static 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 */
 262static 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 */
 292static 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 */
 304static 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 */
 346u32 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}
 445