linux/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of version 2 of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12 * more details.
  13 *
  14 ******************************************************************************/
  15#define _RTL8723BS_RECV_C_
  16
  17#include <drv_types.h>
  18#include <rtw_debug.h>
  19#include <rtl8723b_hal.h>
  20
  21static s32 initrecvbuf(struct recv_buf *precvbuf, struct adapter *padapter)
  22{
  23        INIT_LIST_HEAD(&precvbuf->list);
  24        spin_lock_init(&precvbuf->recvbuf_lock);
  25
  26        precvbuf->adapter = padapter;
  27
  28        return _SUCCESS;
  29}
  30
  31static void update_recvframe_attrib(struct adapter *padapter,
  32                                    union recv_frame *precvframe,
  33                                    struct recv_stat *prxstat)
  34{
  35        struct rx_pkt_attrib *pattrib;
  36        struct recv_stat report;
  37        PRXREPORT prxreport = (PRXREPORT)&report;
  38
  39        report.rxdw0 = prxstat->rxdw0;
  40        report.rxdw1 = prxstat->rxdw1;
  41        report.rxdw2 = prxstat->rxdw2;
  42        report.rxdw3 = prxstat->rxdw3;
  43        report.rxdw4 = prxstat->rxdw4;
  44        report.rxdw5 = prxstat->rxdw5;
  45
  46        pattrib = &precvframe->u.hdr.attrib;
  47        memset(pattrib, 0, sizeof(struct rx_pkt_attrib));
  48
  49        /*  update rx report to recv_frame attribute */
  50        pattrib->pkt_rpt_type = prxreport->c2h_ind ? C2H_PACKET : NORMAL_RX;
  51/*      DBG_871X("%s: pkt_rpt_type =%d\n", __func__, pattrib->pkt_rpt_type); */
  52
  53        if (pattrib->pkt_rpt_type == NORMAL_RX) {
  54                /*  Normal rx packet */
  55                /*  update rx report to recv_frame attribute */
  56                pattrib->pkt_len = (u16)prxreport->pktlen;
  57                pattrib->drvinfo_sz = (u8)(prxreport->drvinfosize << 3);
  58                pattrib->physt = (u8)prxreport->physt;
  59
  60                pattrib->crc_err = (u8)prxreport->crc32;
  61                pattrib->icv_err = (u8)prxreport->icverr;
  62
  63                pattrib->bdecrypted = (u8)(prxreport->swdec ? 0 : 1);
  64                pattrib->encrypt = (u8)prxreport->security;
  65
  66                pattrib->qos = (u8)prxreport->qos;
  67                pattrib->priority = (u8)prxreport->tid;
  68
  69                pattrib->amsdu = (u8)prxreport->amsdu;
  70
  71                pattrib->seq_num = (u16)prxreport->seq;
  72                pattrib->frag_num = (u8)prxreport->frag;
  73                pattrib->mfrag = (u8)prxreport->mf;
  74                pattrib->mdata = (u8)prxreport->md;
  75
  76                pattrib->data_rate = (u8)prxreport->rx_rate;
  77        } else {
  78                pattrib->pkt_len = (u16)prxreport->pktlen;
  79        }
  80}
  81
  82/*
  83 * Notice:
  84 *Before calling this function,
  85 *precvframe->u.hdr.rx_data should be ready!
  86 */
  87static void update_recvframe_phyinfo(union recv_frame *precvframe,
  88                                     struct phy_stat *pphy_status)
  89{
  90        struct adapter *padapter = precvframe->u.hdr.adapter;
  91        struct rx_pkt_attrib *pattrib = &precvframe->u.hdr.attrib;
  92        struct hal_com_data *p_hal_data = GET_HAL_DATA(padapter);
  93        struct odm_phy_info *p_phy_info =
  94                (struct odm_phy_info *)(&pattrib->phy_info);
  95
  96        u8 *wlanhdr;
  97        u8 *my_bssid;
  98        u8 *rx_bssid;
  99        u8 *rx_ra;
 100        u8 *my_hwaddr;
 101        u8 *sa = NULL;
 102
 103        struct odm_packet_info pkt_info = {
 104                .data_rate   = 0x00,
 105                .station_id  = 0x00,
 106                .bssid_match = false,
 107                .to_self     = false,
 108                .is_beacon   = false,
 109        };
 110
 111        /* _irqL                irqL; */
 112        struct sta_priv *pstapriv;
 113        struct sta_info *psta;
 114
 115        wlanhdr = get_recvframe_data(precvframe);
 116        my_bssid = get_bssid(&padapter->mlmepriv);
 117        rx_bssid = get_hdr_bssid(wlanhdr);
 118        pkt_info.bssid_match = ((!IsFrameTypeCtrl(wlanhdr)) &&
 119                                !pattrib->icv_err && !pattrib->crc_err &&
 120                                !ether_addr_equal(rx_bssid, my_bssid));
 121
 122        rx_ra = get_ra(wlanhdr);
 123        my_hwaddr = myid(&padapter->eeprompriv);
 124        pkt_info.to_self = pkt_info.bssid_match &&
 125                !ether_addr_equal(rx_ra, my_hwaddr);
 126
 127
 128        pkt_info.is_beacon = pkt_info.bssid_match &&
 129                (GetFrameSubType(wlanhdr) == WIFI_BEACON);
 130
 131        sa = get_ta(wlanhdr);
 132
 133        pkt_info.station_id = 0xFF;
 134
 135        pstapriv = &padapter->stapriv;
 136        psta = rtw_get_stainfo(pstapriv, sa);
 137        if (psta) {
 138                pkt_info.station_id = psta->mac_id;
 139                /* DBG_8192C("%s ==> StationID(%d)\n",
 140                 *        __func__, pkt_info.station_id); */
 141        }
 142        pkt_info.data_rate = pattrib->data_rate;
 143
 144        /* rtl8723b_query_rx_phy_status(precvframe, pphy_status); */
 145        /* spin_lock_bh(&p_hal_data->odm_stainfo_lock); */
 146        ODM_PhyStatusQuery(&p_hal_data->odmpriv, p_phy_info,
 147                           (u8 *)pphy_status, &(pkt_info));
 148        if (psta)
 149                psta->rssi = pattrib->phy_info.RecvSignalPower;
 150        /* spin_unlock_bh(&p_hal_data->odm_stainfo_lock); */
 151        precvframe->u.hdr.psta = NULL;
 152        if (
 153                pkt_info.bssid_match &&
 154                (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)
 155        ) {
 156                if (psta) {
 157                        precvframe->u.hdr.psta = psta;
 158                        rtl8723b_process_phy_info(padapter, precvframe);
 159                }
 160        } else if (pkt_info.to_self || pkt_info.is_beacon) {
 161                u32 adhoc_state = WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE;
 162                if (check_fwstate(&padapter->mlmepriv, adhoc_state))
 163                        if (psta)
 164                                precvframe->u.hdr.psta = psta;
 165                rtl8723b_process_phy_info(padapter, precvframe);
 166        }
 167}
 168
 169static void rtl8723bs_c2h_packet_handler(struct adapter *padapter,
 170                                         u8 *pbuf, u16 length)
 171{
 172        u8 *tmp = NULL;
 173        u8 res = false;
 174
 175        if (length == 0)
 176                return;
 177
 178        /* DBG_871X("+%s() length =%d\n", __func__, length); */
 179
 180        tmp = rtw_zmalloc(length);
 181        if (tmp == NULL)
 182                return;
 183
 184        memcpy(tmp, pbuf, length);
 185
 186        res = rtw_c2h_packet_wk_cmd(padapter, tmp, length);
 187
 188        if (res == false)
 189                kfree(tmp);
 190
 191        /* DBG_871X("-%s res(%d)\n", __func__, res); */
 192
 193        return;
 194}
 195
 196static inline union recv_frame *try_alloc_recvframe(struct recv_priv *precvpriv,
 197                                                    struct recv_buf *precvbuf)
 198{
 199        union recv_frame *precvframe;
 200
 201        precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
 202        if (!precvframe) {
 203                DBG_8192C("%s: no enough recv frame!\n", __func__);
 204                rtw_enqueue_recvbuf_to_head(precvbuf,
 205                                            &precvpriv->recv_buf_pending_queue);
 206
 207                /*  The case of can't allocte recvframe should be temporary, */
 208                /*  schedule again and hope recvframe is available next time. */
 209                tasklet_schedule(&precvpriv->recv_tasklet);
 210        }
 211
 212        return precvframe;
 213}
 214
 215static inline bool rx_crc_err(struct recv_priv *precvpriv,
 216                              struct hal_com_data *p_hal_data,
 217                              struct rx_pkt_attrib *pattrib,
 218                              union recv_frame *precvframe)
 219{
 220        /*  fix Hardware RX data error, drop whole recv_buffer */
 221        if ((!(p_hal_data->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err) {
 222                DBG_8192C("%s()-%d: RX Warning! rx CRC ERROR !!\n",
 223                          __func__, __LINE__);
 224                rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
 225                return true;
 226        }
 227
 228        return false;
 229}
 230
 231static inline bool pkt_exceeds_tail(struct recv_priv *precvpriv,
 232                                    u8 *end, u8 *tail,
 233                                    union recv_frame *precvframe)
 234{
 235        if (end > tail) {
 236                DBG_8192C("%s()-%d: : next pkt len(%p,%d) exceed ptail(%p)!\n",
 237                          __func__, __LINE__, ptr, pkt_offset, precvbuf->ptail);
 238                rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
 239                return true;
 240        }
 241
 242        return false;
 243}
 244
 245static void rtl8723bs_recv_tasklet(void *priv)
 246{
 247        struct adapter *padapter;
 248        struct hal_com_data *p_hal_data;
 249        struct recv_priv *precvpriv;
 250        struct recv_buf *precvbuf;
 251        union recv_frame *precvframe;
 252        struct rx_pkt_attrib *pattrib;
 253        struct __queue *recv_buf_queue;
 254        u8 *ptr;
 255        u32 pkt_offset, skb_len, alloc_sz;
 256        _pkt *pkt_copy = NULL;
 257        u8 shift_sz = 0, rx_report_sz = 0;
 258
 259        padapter = priv;
 260        p_hal_data = GET_HAL_DATA(padapter);
 261        precvpriv = &padapter->recvpriv;
 262        recv_buf_queue = &precvpriv->recv_buf_pending_queue;
 263
 264        do {
 265                precvbuf = rtw_dequeue_recvbuf(recv_buf_queue);
 266                if (!precvbuf)
 267                        break;
 268
 269                ptr = precvbuf->pdata;
 270
 271                while (ptr < precvbuf->ptail) {
 272                        precvframe = try_alloc_recvframe(precvpriv, precvbuf);
 273                        if(!precvframe)
 274                                return;
 275
 276                        /* rx desc parsing */
 277                        update_recvframe_attrib(padapter, precvframe,
 278                                                (struct recv_stat *)ptr);
 279
 280                        pattrib = &precvframe->u.hdr.attrib;
 281
 282                        if(rx_crc_err(precvpriv, p_hal_data,
 283                                      pattrib, precvframe))
 284                                break;
 285
 286                        rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz;
 287                        pkt_offset = rx_report_sz +
 288                                pattrib->shift_sz +
 289                                pattrib->pkt_len;
 290
 291                        if(pkt_exceeds_tail(precvpriv, ptr + pkt_offset,
 292                                            precvbuf->ptail, precvframe))
 293                                break;
 294
 295                        if ((pattrib->crc_err) || (pattrib->icv_err)) {
 296                                DBG_8192C("%s: crc_err =%d icv_err =%d, skip!\n",
 297                                          __func__, pattrib->crc_err,
 298                                          pattrib->icv_err);
 299                                rtw_free_recvframe(precvframe,
 300                                                   &precvpriv->free_recv_queue);
 301                        } else {
 302                                /*      Modified by Albert 20101213 */
 303                                /*      For 8 bytes IP header alignment. */
 304                                if (pattrib->qos)       /*      Qos data, wireless lan header length is 26 */
 305                                        shift_sz = 6;
 306                                else
 307                                        shift_sz = 0;
 308
 309                                skb_len = pattrib->pkt_len;
 310
 311                                /*  for first fragment packet, driver need allocate 1536+drvinfo_sz+RXDESC_SIZE to defrag packet. */
 312                                /*  modify alloc_sz for recvive crc error packet by thomas 2011-06-02 */
 313                                if ((pattrib->mfrag == 1) && (pattrib->frag_num == 0)) {
 314                                        if (skb_len <= 1650)
 315                                                alloc_sz = 1664;
 316                                        else
 317                                                alloc_sz = skb_len + 14;
 318                                } else {
 319                                        alloc_sz = skb_len;
 320                                        /*      6 is for IP header 8 bytes alignment in QoS packet case. */
 321                                        /*      8 is for skb->data 4 bytes alignment. */
 322                                        alloc_sz += 14;
 323                                }
 324
 325                                pkt_copy = rtw_skb_alloc(alloc_sz);
 326
 327                                if (pkt_copy) {
 328                                        pkt_copy->dev = padapter->pnetdev;
 329                                        precvframe->u.hdr.pkt = pkt_copy;
 330                                        skb_reserve(pkt_copy, 8 - ((SIZE_PTR)(pkt_copy->data) & 7));/* force pkt_copy->data at 8-byte alignment address */
 331                                        skb_reserve(pkt_copy, shift_sz);/* force ip_hdr at 8-byte alignment address according to shift_sz. */
 332                                        memcpy(pkt_copy->data, (ptr + rx_report_sz + pattrib->shift_sz), skb_len);
 333                                        precvframe->u.hdr.rx_head = pkt_copy->head;
 334                                        precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail = pkt_copy->data;
 335                                        precvframe->u.hdr.rx_end = skb_end_pointer(pkt_copy);
 336                                } else {
 337                                        if ((pattrib->mfrag == 1) && (pattrib->frag_num == 0)) {
 338                                                DBG_8192C("%s: alloc_skb fail, drop frag frame\n", __func__);
 339                                                rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
 340                                                break;
 341                                        }
 342
 343                                        precvframe->u.hdr.pkt = rtw_skb_clone(precvbuf->pskb);
 344                                        if (precvframe->u.hdr.pkt) {
 345                                                _pkt *pkt_clone = precvframe->u.hdr.pkt;
 346
 347                                                pkt_clone->data = ptr + rx_report_sz + pattrib->shift_sz;
 348                                                skb_reset_tail_pointer(pkt_clone);
 349                                                precvframe->u.hdr.rx_head = precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail
 350                                                        = pkt_clone->data;
 351                                                precvframe->u.hdr.rx_end = pkt_clone->data + skb_len;
 352                                        } else {
 353                                                DBG_8192C("%s: rtw_skb_clone fail\n", __func__);
 354                                                rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
 355                                                break;
 356                                        }
 357                                }
 358
 359                                recvframe_put(precvframe, skb_len);
 360                                /* recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE); */
 361
 362                                if (p_hal_data->ReceiveConfig & RCR_APPFCS)
 363                                        recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
 364
 365                                /*  move to drv info position */
 366                                ptr += RXDESC_SIZE;
 367
 368                                /*  update drv info */
 369                                if (p_hal_data->ReceiveConfig & RCR_APP_BA_SSN) {
 370                                        /* rtl8723s_update_bassn(padapter, pdrvinfo); */
 371                                        ptr += 4;
 372                                }
 373
 374                                if (pattrib->pkt_rpt_type == NORMAL_RX) { /* Normal rx packet */
 375                                        if (pattrib->physt)
 376                                                update_recvframe_phyinfo(precvframe, (struct phy_stat *)ptr);
 377
 378                                        if (rtw_recv_entry(precvframe) != _SUCCESS) {
 379                                                RT_TRACE(_module_rtl871x_recv_c_, _drv_dump_, ("%s: rtw_recv_entry(precvframe) != _SUCCESS\n", __func__));
 380                                        }
 381                                } else if (pattrib->pkt_rpt_type == C2H_PACKET) {
 382                                        C2H_EVT_HDR     C2hEvent;
 383
 384                                        u16 len_c2h = pattrib->pkt_len;
 385                                        u8 *pbuf_c2h = precvframe->u.hdr.rx_data;
 386                                        u8 *pdata_c2h;
 387
 388                                        C2hEvent.CmdID = pbuf_c2h[0];
 389                                        C2hEvent.CmdSeq = pbuf_c2h[1];
 390                                        C2hEvent.CmdLen = (len_c2h-2);
 391                                        pdata_c2h = pbuf_c2h+2;
 392
 393                                        if (C2hEvent.CmdID == C2H_CCX_TX_RPT)
 394                                                CCX_FwC2HTxRpt_8723b(padapter, pdata_c2h, C2hEvent.CmdLen);
 395                                        else
 396                                                rtl8723bs_c2h_packet_handler(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
 397
 398                                        rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
 399                                }
 400                        }
 401
 402                        pkt_offset = _RND8(pkt_offset);
 403                        precvbuf->pdata += pkt_offset;
 404                        ptr = precvbuf->pdata;
 405                        precvframe = NULL;
 406                        pkt_copy = NULL;
 407                }
 408
 409                rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
 410        } while (1);
 411}
 412
 413/*
 414 * Initialize recv private variable for hardware dependent
 415 * 1. recv buf
 416 * 2. recv tasklet
 417 *
 418 */
 419s32 rtl8723bs_init_recv_priv(struct adapter *padapter)
 420{
 421        s32 res;
 422        u32 i, n;
 423        struct recv_priv *precvpriv;
 424        struct recv_buf *precvbuf;
 425
 426        res = _SUCCESS;
 427        precvpriv = &padapter->recvpriv;
 428
 429        /* 3 1. init recv buffer */
 430        _rtw_init_queue(&precvpriv->free_recv_buf_queue);
 431        _rtw_init_queue(&precvpriv->recv_buf_pending_queue);
 432
 433        n = NR_RECVBUFF * sizeof(struct recv_buf) + 4;
 434        precvpriv->pallocated_recv_buf = rtw_zmalloc(n);
 435        if (precvpriv->pallocated_recv_buf == NULL) {
 436                res = _FAIL;
 437                RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("alloc recv_buf fail!\n"));
 438                goto exit;
 439        }
 440
 441        precvpriv->precv_buf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(precvpriv->pallocated_recv_buf), 4);
 442
 443        /*  init each recv buffer */
 444        precvbuf = (struct recv_buf *)precvpriv->precv_buf;
 445        for (i = 0; i < NR_RECVBUFF; i++) {
 446                res = initrecvbuf(precvbuf, padapter);
 447                if (res == _FAIL)
 448                        break;
 449
 450                if (precvbuf->pskb == NULL) {
 451                        SIZE_PTR tmpaddr = 0;
 452                        SIZE_PTR alignment = 0;
 453
 454                        precvbuf->pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);
 455
 456                        if (precvbuf->pskb) {
 457                                precvbuf->pskb->dev = padapter->pnetdev;
 458
 459                                tmpaddr = (SIZE_PTR)precvbuf->pskb->data;
 460                                alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
 461                                skb_reserve(precvbuf->pskb, (RECVBUFF_ALIGN_SZ - alignment));
 462                        }
 463
 464                        if (precvbuf->pskb == NULL) {
 465                                DBG_871X("%s: alloc_skb fail!\n", __func__);
 466                        }
 467                }
 468
 469                list_add_tail(&precvbuf->list, &precvpriv->free_recv_buf_queue.queue);
 470
 471                precvbuf++;
 472        }
 473        precvpriv->free_recv_buf_queue_cnt = i;
 474
 475        if (res == _FAIL)
 476                goto initbuferror;
 477
 478        /* 3 2. init tasklet */
 479        tasklet_init(
 480                &precvpriv->recv_tasklet,
 481                (void(*)(unsigned long))rtl8723bs_recv_tasklet,
 482                (unsigned long)padapter
 483        );
 484
 485        goto exit;
 486
 487initbuferror:
 488        precvbuf = (struct recv_buf *)precvpriv->precv_buf;
 489        if (precvbuf) {
 490                n = precvpriv->free_recv_buf_queue_cnt;
 491                precvpriv->free_recv_buf_queue_cnt = 0;
 492                for (i = 0; i < n ; i++) {
 493                        list_del_init(&precvbuf->list);
 494                        rtw_os_recvbuf_resource_free(padapter, precvbuf);
 495                        precvbuf++;
 496                }
 497                precvpriv->precv_buf = NULL;
 498        }
 499
 500        if (precvpriv->pallocated_recv_buf) {
 501                n = NR_RECVBUFF * sizeof(struct recv_buf) + 4;
 502                kfree(precvpriv->pallocated_recv_buf);
 503                precvpriv->pallocated_recv_buf = NULL;
 504        }
 505
 506exit:
 507        return res;
 508}
 509
 510/*
 511 * Free recv private variable of hardware dependent
 512 * 1. recv buf
 513 * 2. recv tasklet
 514 *
 515 */
 516void rtl8723bs_free_recv_priv(struct adapter *padapter)
 517{
 518        u32 i, n;
 519        struct recv_priv *precvpriv;
 520        struct recv_buf *precvbuf;
 521
 522        precvpriv = &padapter->recvpriv;
 523
 524        /* 3 1. kill tasklet */
 525        tasklet_kill(&precvpriv->recv_tasklet);
 526
 527        /* 3 2. free all recv buffers */
 528        precvbuf = (struct recv_buf *)precvpriv->precv_buf;
 529        if (precvbuf) {
 530                n = NR_RECVBUFF;
 531                precvpriv->free_recv_buf_queue_cnt = 0;
 532                for (i = 0; i < n ; i++) {
 533                        list_del_init(&precvbuf->list);
 534                        rtw_os_recvbuf_resource_free(padapter, precvbuf);
 535                        precvbuf++;
 536                }
 537                precvpriv->precv_buf = NULL;
 538        }
 539
 540        if (precvpriv->pallocated_recv_buf) {
 541                n = NR_RECVBUFF * sizeof(struct recv_buf) + 4;
 542                kfree(precvpriv->pallocated_recv_buf);
 543                precvpriv->pallocated_recv_buf = NULL;
 544        }
 545}
 546