root/drivers/staging/rtl8192e/rtl819x_TSProc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. TsSetupTimeOut
  2. TsInactTimeout
  3. RxPktPendingTimeout
  4. TsAddBaProcess
  5. ResetTsCommonInfo
  6. ResetTxTsEntry
  7. ResetRxTsEntry
  8. TSInitialize
  9. AdmitTS
  10. SearchAdmitTRStream
  11. MakeTSEntry
  12. GetTs
  13. RemoveTsEntry
  14. RemovePeerTS
  15. RemoveAllTS
  16. TsStartAddBaProcess

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

/* [<][>][^][v][top][bottom][index][help] */