linux/drivers/staging/rtl8192e/rtl819x_TSProc.c
<<
>>
Prefs
   1/******************************************************************************
   2 * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
   3 *
   4 * This program is distributed in the hope that it will be useful, but WITHOUT
   5 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   6 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   7 * more details.
   8 *
   9 * The full GNU General Public License is included in this distribution in the
  10 * file called LICENSE.
  11 *
  12 * Contact Information:
  13 * wlanfae <wlanfae@realtek.com>
  14 ******************************************************************************/
  15#include "rtllib.h"
  16#include <linux/etherdevice.h>
  17#include "rtl819x_TS.h"
  18
  19static void TsSetupTimeOut(struct timer_list *unused)
  20{
  21}
  22
  23static void TsInactTimeout(struct timer_list *unused)
  24{
  25}
  26
  27static void RxPktPendingTimeout(struct timer_list *t)
  28{
  29        struct rx_ts_record *pRxTs = from_timer(pRxTs, t,
  30                                                     RxPktPendingTimer);
  31        struct rtllib_device *ieee = container_of(pRxTs, struct rtllib_device,
  32                                                  RxTsRecord[pRxTs->num]);
  33
  34        struct rx_reorder_entry *pReorderEntry = NULL;
  35
  36        unsigned long flags = 0;
  37        u8 index = 0;
  38        bool bPktInBuf = false;
  39
  40        spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
  41        if (pRxTs->RxTimeoutIndicateSeq != 0xffff) {
  42                while (!list_empty(&pRxTs->RxPendingPktList)) {
  43                        pReorderEntry = (struct rx_reorder_entry *)
  44                                        list_entry(pRxTs->RxPendingPktList.prev,
  45                                        struct rx_reorder_entry, List);
  46                        if (index == 0)
  47                                pRxTs->RxIndicateSeq = pReorderEntry->SeqNum;
  48
  49                        if (SN_LESS(pReorderEntry->SeqNum,
  50                                    pRxTs->RxIndicateSeq) ||
  51                            SN_EQUAL(pReorderEntry->SeqNum,
  52                                     pRxTs->RxIndicateSeq)) {
  53                                list_del_init(&pReorderEntry->List);
  54
  55                                if (SN_EQUAL(pReorderEntry->SeqNum,
  56                                    pRxTs->RxIndicateSeq))
  57                                        pRxTs->RxIndicateSeq =
  58                                              (pRxTs->RxIndicateSeq + 1) % 4096;
  59
  60                                netdev_dbg(ieee->dev,
  61                                           "%s(): Indicate SeqNum: %d\n",
  62                                           __func__, pReorderEntry->SeqNum);
  63                                ieee->stats_IndicateArray[index] =
  64                                                         pReorderEntry->prxb;
  65                                index++;
  66
  67                                list_add_tail(&pReorderEntry->List,
  68                                              &ieee->RxReorder_Unused_List);
  69                        } else {
  70                                bPktInBuf = true;
  71                                break;
  72                        }
  73                }
  74        }
  75
  76        if (index > 0) {
  77                pRxTs->RxTimeoutIndicateSeq = 0xffff;
  78
  79                if (index > REORDER_WIN_SIZE) {
  80                        netdev_warn(ieee->dev,
  81                                    "%s(): Rx Reorder struct buffer full\n",
  82                                    __func__);
  83                        spin_unlock_irqrestore(&(ieee->reorder_spinlock),
  84                                               flags);
  85                        return;
  86                }
  87                rtllib_indicate_packets(ieee, ieee->stats_IndicateArray, index);
  88                bPktInBuf = false;
  89        }
  90
  91        if (bPktInBuf && (pRxTs->RxTimeoutIndicateSeq == 0xffff)) {
  92                pRxTs->RxTimeoutIndicateSeq = pRxTs->RxIndicateSeq;
  93                mod_timer(&pRxTs->RxPktPendingTimer,  jiffies +
  94                          msecs_to_jiffies(ieee->pHTInfo->RxReorderPendingTime)
  95                          );
  96        }
  97        spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
  98}
  99
 100static void TsAddBaProcess(struct timer_list *t)
 101{
 102        struct tx_ts_record *pTxTs = from_timer(pTxTs, t, TsAddBaTimer);
 103        u8 num = pTxTs->num;
 104        struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device,
 105                                     TxTsRecord[num]);
 106
 107        TsInitAddBA(ieee, pTxTs, BA_POLICY_IMMEDIATE, false);
 108        netdev_dbg(ieee->dev, "%s(): ADDBA Req is started\n", __func__);
 109}
 110
 111static void ResetTsCommonInfo(struct ts_common_info *pTsCommonInfo)
 112{
 113        eth_zero_addr(pTsCommonInfo->Addr);
 114        memset(&pTsCommonInfo->TSpec, 0, sizeof(union tspec_body));
 115        memset(&pTsCommonInfo->TClass, 0, sizeof(union qos_tclas)*TCLAS_NUM);
 116        pTsCommonInfo->TClasProc = 0;
 117        pTsCommonInfo->TClasNum = 0;
 118}
 119
 120static void ResetTxTsEntry(struct tx_ts_record *pTS)
 121{
 122        ResetTsCommonInfo(&pTS->TsCommonInfo);
 123        pTS->TxCurSeq = 0;
 124        pTS->bAddBaReqInProgress = false;
 125        pTS->bAddBaReqDelayed = false;
 126        pTS->bUsingBa = false;
 127        pTS->bDisable_AddBa = false;
 128        ResetBaEntry(&pTS->TxAdmittedBARecord);
 129        ResetBaEntry(&pTS->TxPendingBARecord);
 130}
 131
 132static void ResetRxTsEntry(struct rx_ts_record *pTS)
 133{
 134        ResetTsCommonInfo(&pTS->TsCommonInfo);
 135        pTS->RxIndicateSeq = 0xffff;
 136        pTS->RxTimeoutIndicateSeq = 0xffff;
 137        ResetBaEntry(&pTS->RxAdmittedBARecord);
 138}
 139
 140void TSInitialize(struct rtllib_device *ieee)
 141{
 142        struct tx_ts_record *pTxTS  = ieee->TxTsRecord;
 143        struct rx_ts_record *pRxTS  = ieee->RxTsRecord;
 144        struct rx_reorder_entry *pRxReorderEntry = ieee->RxReorderEntry;
 145        u8                              count = 0;
 146
 147        netdev_vdbg(ieee->dev, "%s()\n", __func__);
 148        INIT_LIST_HEAD(&ieee->Tx_TS_Admit_List);
 149        INIT_LIST_HEAD(&ieee->Tx_TS_Pending_List);
 150        INIT_LIST_HEAD(&ieee->Tx_TS_Unused_List);
 151
 152        for (count = 0; count < TOTAL_TS_NUM; count++) {
 153                pTxTS->num = count;
 154                timer_setup(&pTxTS->TsCommonInfo.SetupTimer, TsSetupTimeOut,
 155                            0);
 156
 157                timer_setup(&pTxTS->TsCommonInfo.InactTimer, TsInactTimeout,
 158                            0);
 159
 160                timer_setup(&pTxTS->TsAddBaTimer, TsAddBaProcess, 0);
 161
 162                timer_setup(&pTxTS->TxPendingBARecord.Timer, BaSetupTimeOut,
 163                            0);
 164                timer_setup(&pTxTS->TxAdmittedBARecord.Timer,
 165                            TxBaInactTimeout, 0);
 166
 167                ResetTxTsEntry(pTxTS);
 168                list_add_tail(&pTxTS->TsCommonInfo.List,
 169                                &ieee->Tx_TS_Unused_List);
 170                pTxTS++;
 171        }
 172
 173        INIT_LIST_HEAD(&ieee->Rx_TS_Admit_List);
 174        INIT_LIST_HEAD(&ieee->Rx_TS_Pending_List);
 175        INIT_LIST_HEAD(&ieee->Rx_TS_Unused_List);
 176        for (count = 0; count < TOTAL_TS_NUM; count++) {
 177                pRxTS->num = count;
 178                INIT_LIST_HEAD(&pRxTS->RxPendingPktList);
 179
 180                timer_setup(&pRxTS->TsCommonInfo.SetupTimer, TsSetupTimeOut,
 181                            0);
 182
 183                timer_setup(&pRxTS->TsCommonInfo.InactTimer, TsInactTimeout,
 184                            0);
 185
 186                timer_setup(&pRxTS->RxAdmittedBARecord.Timer,
 187                            RxBaInactTimeout, 0);
 188
 189                timer_setup(&pRxTS->RxPktPendingTimer, RxPktPendingTimeout, 0);
 190
 191                ResetRxTsEntry(pRxTS);
 192                list_add_tail(&pRxTS->TsCommonInfo.List,
 193                              &ieee->Rx_TS_Unused_List);
 194                pRxTS++;
 195        }
 196        INIT_LIST_HEAD(&ieee->RxReorder_Unused_List);
 197        for (count = 0; count < REORDER_ENTRY_NUM; count++) {
 198                list_add_tail(&pRxReorderEntry->List,
 199                              &ieee->RxReorder_Unused_List);
 200                if (count == (REORDER_ENTRY_NUM-1))
 201                        break;
 202                pRxReorderEntry = &ieee->RxReorderEntry[count+1];
 203        }
 204
 205}
 206
 207static void AdmitTS(struct rtllib_device *ieee,
 208                    struct ts_common_info *pTsCommonInfo, u32 InactTime)
 209{
 210        del_timer_sync(&pTsCommonInfo->SetupTimer);
 211        del_timer_sync(&pTsCommonInfo->InactTimer);
 212
 213        if (InactTime != 0)
 214                mod_timer(&pTsCommonInfo->InactTimer, jiffies +
 215                          msecs_to_jiffies(InactTime));
 216}
 217
 218static struct ts_common_info *SearchAdmitTRStream(struct rtllib_device *ieee,
 219                                                  u8 *Addr, u8 TID,
 220                                                  enum tr_select TxRxSelect)
 221{
 222        u8      dir;
 223        bool    search_dir[4] = {0};
 224        struct list_head *psearch_list;
 225        struct ts_common_info *pRet = NULL;
 226
 227        if (ieee->iw_mode == IW_MODE_MASTER) {
 228                if (TxRxSelect == TX_DIR) {
 229                        search_dir[DIR_DOWN] = true;
 230                        search_dir[DIR_BI_DIR] = true;
 231                } else {
 232                        search_dir[DIR_UP] = true;
 233                        search_dir[DIR_BI_DIR] = true;
 234                }
 235        } else if (ieee->iw_mode == IW_MODE_ADHOC) {
 236                if (TxRxSelect == TX_DIR)
 237                        search_dir[DIR_UP] = true;
 238                else
 239                        search_dir[DIR_DOWN] = true;
 240        } else {
 241                if (TxRxSelect == TX_DIR) {
 242                        search_dir[DIR_UP] = true;
 243                        search_dir[DIR_BI_DIR] = true;
 244                        search_dir[DIR_DIRECT] = true;
 245                } else {
 246                        search_dir[DIR_DOWN] = true;
 247                        search_dir[DIR_BI_DIR] = true;
 248                        search_dir[DIR_DIRECT] = true;
 249                }
 250        }
 251
 252        if (TxRxSelect == TX_DIR)
 253                psearch_list = &ieee->Tx_TS_Admit_List;
 254        else
 255                psearch_list = &ieee->Rx_TS_Admit_List;
 256
 257        for (dir = 0; dir <= DIR_BI_DIR; dir++) {
 258                if (!search_dir[dir])
 259                        continue;
 260                list_for_each_entry(pRet, psearch_list, List) {
 261                        if (memcmp(pRet->Addr, Addr, 6) == 0 &&
 262                            pRet->TSpec.f.TSInfo.field.ucTSID == TID &&
 263                            pRet->TSpec.f.TSInfo.field.ucDirection == dir)
 264                                break;
 265
 266                }
 267                if (&pRet->List  != psearch_list)
 268                        break;
 269        }
 270
 271        if (pRet && &pRet->List  != psearch_list)
 272                return pRet;
 273        return NULL;
 274}
 275
 276static void MakeTSEntry(struct ts_common_info *pTsCommonInfo, u8 *Addr,
 277                        union tspec_body *pTSPEC, union qos_tclas *pTCLAS,
 278                        u8 TCLAS_Num, u8 TCLAS_Proc)
 279{
 280        u8      count;
 281
 282        if (pTsCommonInfo == NULL)
 283                return;
 284
 285        memcpy(pTsCommonInfo->Addr, Addr, 6);
 286
 287        if (pTSPEC != NULL)
 288                memcpy((u8 *)(&(pTsCommonInfo->TSpec)), (u8 *)pTSPEC,
 289                        sizeof(union tspec_body));
 290
 291        for (count = 0; count < TCLAS_Num; count++)
 292                memcpy((u8 *)(&(pTsCommonInfo->TClass[count])),
 293                       (u8 *)pTCLAS, sizeof(union qos_tclas));
 294
 295        pTsCommonInfo->TClasProc = TCLAS_Proc;
 296        pTsCommonInfo->TClasNum = TCLAS_Num;
 297}
 298
 299bool GetTs(struct rtllib_device *ieee, struct ts_common_info **ppTS,
 300           u8 *Addr, u8 TID, enum tr_select TxRxSelect, bool bAddNewTs)
 301{
 302        u8      UP = 0;
 303        union tspec_body TSpec;
 304        union qos_tsinfo *pTSInfo = &TSpec.f.TSInfo;
 305        struct list_head *pUnusedList;
 306        struct list_head *pAddmitList;
 307        enum direction_value Dir;
 308
 309        if (is_multicast_ether_addr(Addr)) {
 310                netdev_warn(ieee->dev, "Get TS for Broadcast or Multicast\n");
 311                return false;
 312        }
 313        if (ieee->current_network.qos_data.supported == 0) {
 314                UP = 0;
 315        } else {
 316                switch (TID) {
 317                case 0:
 318                case 3:
 319                        UP = 0;
 320                        break;
 321                case 1:
 322                case 2:
 323                        UP = 2;
 324                        break;
 325                case 4:
 326                case 5:
 327                        UP = 5;
 328                        break;
 329                case 6:
 330                case 7:
 331                        UP = 7;
 332                        break;
 333                default:
 334                        netdev_warn(ieee->dev, "%s(): TID(%d) is not valid\n",
 335                                    __func__, TID);
 336                        return false;
 337                }
 338        }
 339
 340        *ppTS = SearchAdmitTRStream(ieee, Addr, UP, TxRxSelect);
 341        if (*ppTS != NULL)
 342                return true;
 343
 344        if (!bAddNewTs) {
 345                netdev_dbg(ieee->dev, "add new TS failed(tid:%d)\n", UP);
 346                return false;
 347        }
 348
 349        pUnusedList = (TxRxSelect == TX_DIR) ?
 350                                (&ieee->Tx_TS_Unused_List) :
 351                                (&ieee->Rx_TS_Unused_List);
 352
 353        pAddmitList = (TxRxSelect == TX_DIR) ?
 354                                (&ieee->Tx_TS_Admit_List) :
 355                                (&ieee->Rx_TS_Admit_List);
 356
 357        Dir = (ieee->iw_mode == IW_MODE_MASTER) ?
 358                                ((TxRxSelect == TX_DIR) ? DIR_DOWN : DIR_UP) :
 359                                ((TxRxSelect == TX_DIR) ? DIR_UP : DIR_DOWN);
 360
 361        if (!list_empty(pUnusedList)) {
 362                (*ppTS) = list_entry(pUnusedList->next,
 363                          struct ts_common_info, List);
 364                list_del_init(&(*ppTS)->List);
 365                if (TxRxSelect == TX_DIR) {
 366                        struct tx_ts_record *tmp =
 367                                container_of(*ppTS,
 368                                struct tx_ts_record,
 369                                TsCommonInfo);
 370                        ResetTxTsEntry(tmp);
 371                } else {
 372                        struct rx_ts_record *tmp =
 373                                 container_of(*ppTS,
 374                                 struct rx_ts_record,
 375                                 TsCommonInfo);
 376                        ResetRxTsEntry(tmp);
 377                }
 378
 379                netdev_dbg(ieee->dev,
 380                           "to init current TS, UP:%d, Dir:%d, addr: %pM ppTs=%p\n",
 381                           UP, Dir, Addr, *ppTS);
 382                pTSInfo->field.ucTrafficType = 0;
 383                pTSInfo->field.ucTSID = UP;
 384                pTSInfo->field.ucDirection = Dir;
 385                pTSInfo->field.ucAccessPolicy = 1;
 386                pTSInfo->field.ucAggregation = 0;
 387                pTSInfo->field.ucPSB = 0;
 388                pTSInfo->field.ucUP = UP;
 389                pTSInfo->field.ucTSInfoAckPolicy = 0;
 390                pTSInfo->field.ucSchedule = 0;
 391
 392                MakeTSEntry(*ppTS, Addr, &TSpec, NULL, 0, 0);
 393                AdmitTS(ieee, *ppTS, 0);
 394                list_add_tail(&((*ppTS)->List), pAddmitList);
 395
 396                return true;
 397        }
 398
 399        netdev_warn(ieee->dev,
 400                    "There is not enough dir=%d(0=up down=1) TS record to be used!",
 401                    Dir);
 402        return false;
 403}
 404
 405static void RemoveTsEntry(struct rtllib_device *ieee,
 406                          struct ts_common_info *pTs, enum tr_select TxRxSelect)
 407{
 408        del_timer_sync(&pTs->SetupTimer);
 409        del_timer_sync(&pTs->InactTimer);
 410        TsInitDelBA(ieee, pTs, TxRxSelect);
 411
 412        if (TxRxSelect == RX_DIR) {
 413                struct rx_reorder_entry *pRxReorderEntry;
 414                struct rx_ts_record *pRxTS = (struct rx_ts_record *)pTs;
 415
 416                if (timer_pending(&pRxTS->RxPktPendingTimer))
 417                        del_timer_sync(&pRxTS->RxPktPendingTimer);
 418
 419                while (!list_empty(&pRxTS->RxPendingPktList)) {
 420                        pRxReorderEntry = (struct rx_reorder_entry *)
 421                                        list_entry(pRxTS->RxPendingPktList.prev,
 422                                        struct rx_reorder_entry, List);
 423                        netdev_dbg(ieee->dev,  "%s(): Delete SeqNum %d!\n",
 424                                   __func__, pRxReorderEntry->SeqNum);
 425                        list_del_init(&pRxReorderEntry->List);
 426                        {
 427                                int i = 0;
 428                                struct rtllib_rxb *prxb = pRxReorderEntry->prxb;
 429
 430                                if (unlikely(!prxb))
 431                                        return;
 432                                for (i = 0; i < prxb->nr_subframes; i++)
 433                                        dev_kfree_skb(prxb->subframes[i]);
 434                                kfree(prxb);
 435                                prxb = NULL;
 436                        }
 437                        list_add_tail(&pRxReorderEntry->List,
 438                                      &ieee->RxReorder_Unused_List);
 439                }
 440        } else {
 441                struct tx_ts_record *pTxTS = (struct tx_ts_record *)pTs;
 442
 443                del_timer_sync(&pTxTS->TsAddBaTimer);
 444        }
 445}
 446
 447void RemovePeerTS(struct rtllib_device *ieee, u8 *Addr)
 448{
 449        struct ts_common_info *pTS, *pTmpTS;
 450
 451        netdev_info(ieee->dev, "===========>RemovePeerTS, %pM\n", Addr);
 452
 453        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) {
 454                if (memcmp(pTS->Addr, Addr, 6) == 0) {
 455                        RemoveTsEntry(ieee, pTS, TX_DIR);
 456                        list_del_init(&pTS->List);
 457                        list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
 458                }
 459        }
 460
 461        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List) {
 462                if (memcmp(pTS->Addr, Addr, 6) == 0) {
 463                        netdev_info(ieee->dev,
 464                                    "====>remove Tx_TS_admin_list\n");
 465                        RemoveTsEntry(ieee, pTS, TX_DIR);
 466                        list_del_init(&pTS->List);
 467                        list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
 468                }
 469        }
 470
 471        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List) {
 472                if (memcmp(pTS->Addr, Addr, 6) == 0) {
 473                        RemoveTsEntry(ieee, pTS, RX_DIR);
 474                        list_del_init(&pTS->List);
 475                        list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
 476                }
 477        }
 478
 479        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List) {
 480                if (memcmp(pTS->Addr, Addr, 6) == 0) {
 481                        RemoveTsEntry(ieee, pTS, RX_DIR);
 482                        list_del_init(&pTS->List);
 483                        list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
 484                }
 485        }
 486}
 487EXPORT_SYMBOL(RemovePeerTS);
 488
 489void RemoveAllTS(struct rtllib_device *ieee)
 490{
 491        struct ts_common_info *pTS, *pTmpTS;
 492
 493        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) {
 494                RemoveTsEntry(ieee, pTS, TX_DIR);
 495                list_del_init(&pTS->List);
 496                list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
 497        }
 498
 499        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List) {
 500                RemoveTsEntry(ieee, pTS, TX_DIR);
 501                list_del_init(&pTS->List);
 502                list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
 503        }
 504
 505        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List) {
 506                RemoveTsEntry(ieee, pTS, RX_DIR);
 507                list_del_init(&pTS->List);
 508                list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
 509        }
 510
 511        list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List) {
 512                RemoveTsEntry(ieee, pTS, RX_DIR);
 513                list_del_init(&pTS->List);
 514                list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
 515        }
 516}
 517
 518void TsStartAddBaProcess(struct rtllib_device *ieee, struct tx_ts_record *pTxTS)
 519{
 520        if (pTxTS->bAddBaReqInProgress == false) {
 521                pTxTS->bAddBaReqInProgress = true;
 522
 523                if (pTxTS->bAddBaReqDelayed) {
 524                        netdev_dbg(ieee->dev, "Start ADDBA after 60 sec!!\n");
 525                        mod_timer(&pTxTS->TsAddBaTimer, jiffies +
 526                                  msecs_to_jiffies(TS_ADDBA_DELAY));
 527                } else {
 528                        netdev_dbg(ieee->dev, "Immediately Start ADDBA\n");
 529                        mod_timer(&pTxTS->TsAddBaTimer, jiffies+10);
 530                }
 531        } else
 532                netdev_dbg(ieee->dev, "BA timer is already added\n");
 533}
 534