openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

ctrans.c (48348B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * File:        ctecho.c
      8  * Purpose:     CPU transport setup and echo routines
      9  * Requires:    
     10  */
     11 
     12 
     13 #include <sal/types.h>
     14 #include <sal/core/libc.h>
     15 #include <sal/core/thread.h>
     16 #include <sal/core/dpc.h>
     17 #include <sal/appl/sal.h>
     18 #include <sal/appl/io.h>
     19 #include <shared/bsl.h>
     20 #include <bcm/stack.h>
     21 
     22 #include <appl/diag/shell.h>
     23 #include <appl/diag/system.h>
     24 #include <appl/diag/parse.h>
     25 #include <appl/diag/ctrans.h>
     26 
     27 #include <appl/cpudb/cpudb.h>
     28 #include <appl/cputrans/cputrans.h>
     29 #include <appl/cputrans/atp.h>
     30 #include <appl/cputrans/next_hop.h>
     31 #include <appl/cputrans/cpu2cpu.h>
     32 #include <appl/cputrans/atptrans_socket.h>
     33 #if defined(BCM_RPC_SUPPORT)
     34 #include <bcm/pkt.h>
     35 #include <appl/cputrans/ct_tun.h>
     36 #endif
     37 
     38 #if defined(BROADCOM_DEBUG)
     39 extern void atp_dump(int);
     40 extern void c2c_dump(void);
     41 #endif /* BROADCOM_DEBUG */
     42 
     43 
     44 #if defined(INCLUDE_LIB_CPUDB) && defined(INCLUDE_LIB_CPUTRANS) && \
     45     defined(INCLUDE_LIB_DISCOVER) && defined(INCLUDE_LIB_STKTASK)
     46 
     47 
     48 /*
     49  * The following routine is used to register with BCM RX in
     50  * order to initiate BCM RX packet processing.
     51  */
     52 #define RX_PKT_CLEAR_USEC       250000  /* Time to wait for RX discard */ 
     53 #define RX_PROCESS_START_PRIO        1  /* Callback routine prio */
     54 
     55 STATIC bcm_rx_t
     56 rx_process_start(int unit, bcm_pkt_t *pkt, void *cookie)
     57 {
     58     COMPILER_REFERENCE(unit);
     59     COMPILER_REFERENCE(pkt);
     60     COMPILER_REFERENCE(cookie);
     61     
     62     return BCM_RX_NOT_HANDLED;
     63 }
     64 
     65 /*
     66  * Start BCM RX and initiate packet processing
     67  *
     68  * Note:
     69  *   BCM RX does not start packet processing until a callback
     70  *   routine is registered (other than the internal discard routine).
     71  */
     72 STATIC int
     73 rx_start(void)
     74 {
     75     int  i;
     76     int  rv = BCM_E_NONE;
     77     int  rx_started = 0;
     78     /*
     79      * Start BCM RX
     80      */
     81     for (i = 0; i < soc_ndev; i++) {
     82         if (bcm_rx_active(SOC_NDEV_IDX2DEV(i))) {
     83             rx_started++;
     84             continue;
     85         }
     86         rv = bcm_rx_init(SOC_NDEV_IDX2DEV(i));
     87         if (rv == BCM_E_UNAVAIL) {
     88             cli_out("WARNING: BCM RX init failed, unit %d: %s\n",
     89                     SOC_NDEV_IDX2DEV(i), bcm_errmsg(rv));
     90             continue;
     91         } else if (BCM_FAILURE(rv)) {
     92             cli_out("ERROR: BCM RX init failed, unit %d: %s\n",
     93                     SOC_NDEV_IDX2DEV(i), bcm_errmsg(rv));
     94             return rv;
     95         }
     96 
     97         rv = bcm_rx_start(SOC_NDEV_IDX2DEV(i), NULL);
     98         if (BCM_FAILURE(rv)) {
     99             cli_out("ERROR: BCM RX start failed, unit %d: %s\n",
    100                     SOC_NDEV_IDX2DEV(i), bcm_errmsg(rv));
    101             return rv;
    102         }
    103         rx_started++;
    104     }
    105 
    106     /* Error if RX did not get started on any unit */
    107     if (rx_started == 0) {
    108         return BCM_E_UNAVAIL;
    109     }
    110     
    111     /*
    112      * Start BCM RX packet processing
    113      */
    114     
    115     /* RX packet processing has already started if NH is running */
    116     if (next_hop_running()) {
    117         return BCM_E_NONE;
    118     }
    119      
    120     /* Register dummy callback to trigger BCM RX packet processing */
    121     for (i = 0; i < soc_ndev; i++) {
    122 
    123         /* Skip units that did not get started */
    124         if (!bcm_rx_active(SOC_NDEV_IDX2DEV(i))) {
    125             cli_out("WARNING: skipping callback registration on unit %d\n", SOC_NDEV_IDX2DEV(i));
    126             continue;
    127         }
    128 
    129         rv = bcm_rx_register(SOC_NDEV_IDX2DEV(i), "process_start",
    130                              rx_process_start, RX_PROCESS_START_PRIO,
    131                              NULL, BCM_RCO_F_ALL_COS);
    132         if (BCM_FAILURE(rv)) {
    133             cli_out("ERROR: Cannot register RX callback to initiate packet "
    134                     "processing, unit %d: %s\n",
    135                     SOC_NDEV_IDX2DEV(i), bcm_errmsg(rv));
    136             /* Clean-up */
    137             for (; i > 0; i--) {
    138                 bcm_rx_unregister(SOC_NDEV_IDX2DEV(i), rx_process_start,
    139                                   RX_PROCESS_START_PRIO);
    140             }
    141             return rv;
    142         }
    143     }
    144     
    145     /* Give time for RX handler to process and discard queued packets */
    146     sal_usleep(RX_PKT_CLEAR_USEC);
    147 
    148     /* Routine no longer needed after RX packet processing has started */
    149     for (i = 0; i < soc_ndev; i++) {
    150         bcm_rx_unregister(SOC_NDEV_IDX2DEV(i), rx_process_start, RX_PROCESS_START_PRIO);
    151     }
    152 
    153     return rv;
    154 }
    155 
    156 STATIC int
    157 pkt_preinit(void)
    158 {
    159     int rv;
    160 
    161     /* Start BCM RX and packet processing */
    162     if ((rv = rx_start()) < 0) {
    163         return -1;
    164     }
    165 
    166     if (!cputrans_tx_setup_done()) {
    167         
    168         rv = cputrans_tx_pkt_setup(100, &bcm_trans_ptr);
    169         if (rv < 0) {
    170             cli_out("ERROR: cputrans TX setup failed: %s\n",
    171                     bcm_errmsg(rv));
    172             return -1;
    173         }
    174     }
    175 
    176     if (!cputrans_rx_setup_done()) {
    177         rv = cputrans_rx_pkt_setup(-1, NULL);  /* Use defaults */
    178         if (rv < 0) {
    179             cli_out("ERROR: cputrans RX setup failed: %s\n",
    180                     bcm_errmsg(rv));
    181             return -1;
    182         }
    183     }
    184 
    185     return 0;
    186 }
    187 
    188 
    189 /****************************************************************
    190  *
    191  * CPUTRANS setup
    192  */
    193 
    194 char ct_setup_usage[] =
    195     "CTSetup <subcmd> [options]\n"
    196     "    Modify the CPUTRANS subsystem\n"
    197 #ifndef COMPILER_STRING_CONST_LIMIT
    198     "    Subcommands:\n"
    199     "        <none>                  Report if running\n"
    200     "        BCMREG                  Register BCM transport pointers\n"
    201     "        TXLIMIT [COS=<n> Limit=<n>]\n"
    202     "             Set TX packet alloc limit for the given cos\n"
    203     "        RX START\n"
    204     "             Start packet RX\n"
    205     "        NEXTHOP [DBidx=<n>] [THRDpri=<n>] [RXpri=<n>]\n"
    206     "             [COS=<n>] [VLAN=<n>]\n"
    207     "             Start next hop with the given DB index\n"
    208     "        ATP [TXPri=<n>] [RXPri=<n>] [COS=<n>] [VLAN=<n>]\n"
    209     "             Start ATP with given thread priorities\n"
    210 #if defined(BROADCOM_DEBUG)
    211     "        ATP SHOW\n"
    212     "             Display internal ATP information\n"
    213 #endif  /* BROADCOM_DEBUG */
    214 #ifdef INCLUDE_ATPTRANS_SOCKET
    215     "        ATP TRANSport SOCKet INStall DestKey=<key> DestIP=<ip-addr>\n"
    216     "             Install socket interface for ATP transport\n"
    217     "        ATP TRANSport SOCKet UNINStall DestKey=<key>\n"
    218     "             Uninstall socket interface for ATP transport\n"
    219     "        ATP TRANSport SOCKet SERVer {START | STOP}\n"
    220     "             Start/stop ATP-over-Sockets Server\n"
    221     "        ATP TRANSport SOCKet CONFIG PRIority=<pri> \
    222         ListenPORT=<port-num> ConnectionPORT=<port-num>\n"
    223     "             Configure socket interface for ATP transport\n"
    224 #ifdef BROADCOM_DEBUG
    225     "        ATP TRANSport SOCKet SHOW\n"
    226     "             Display internal ATP-over-Sockets subsystem information\n"
    227 #endif /* BROADCOM_DEBUG */
    228 #endif /* INCLUDE_ATPTRANS_SOCKET */
    229 #if defined(BROADCOM_DEBUG)
    230     "        C2C SHOW\n"
    231     "             Display internal C2C information\n"
    232 #endif  /* BROADCOM_DEBUG */
    233     "        STOP                    Stop ATP\n"
    234     "        TIMEOUT [RetryTO=<n>] [RETRIES=<n>]\n"
    235     "             Set/get the timeout (in usecs) and number of retries\n"
    236     "        SEGLEN [<n>]            Set/get the segment length\n"
    237     "        POOLSIZE [TX=<n>] [RX=<n>]   "
    238     "             Set/get the TX/RX pool size\n"
    239 #endif
    240 #if defined(BCM_RPC_SUPPORT)
    241     "        TUNNEL mode [{RELIABLE | BESTEFFORT}]\n"
    242     "             Set/get the default rx tunnel mode\n"
    243     "        TUNNEL set\n"
    244     "             Create tx tunnel\n"
    245     "        TUNNEL destroy\n"
    246     "             Tear down tx tunnel\n"
    247 #endif
    248     ;
    249 
    250 
    251 #ifdef INCLUDE_ATPTRANS_SOCKET
    252 
    253 STATIC cmd_result_t
    254 atptrans_socket_subcmd(int unit, args_t *args, cpudb_ref_t db_ref)
    255 {
    256     char           *subcmd;
    257     parse_table_t  pt;
    258     cpudb_key_t    local_key;
    259     cpudb_key_t    dest_key;
    260     bcm_ip_t       dest_ip;
    261     int            rv = BCM_E_NONE;
    262 
    263     if ((subcmd = ARG_GET(args)) == NULL) {
    264         cli_out("Need option\n");
    265         return CMD_USAGE;
    266     }
    267                     
    268     if (parse_cmp("INStall", subcmd, '\0')) {
    269 
    270         /* Get destination CPU key and IP address */
    271         parse_table_init(unit, &pt);
    272         parse_table_add(&pt, "DestKey", PQ_DFL|PQ_KEY, 0, 
    273                         dest_key.key, 0);
    274         parse_table_add(&pt, "DestIP", PQ_DFL|PQ_IP, 0,
    275                         (void *)&dest_ip, 0);
    276         if (parse_arg_eq(args, &pt) < 2) {
    277             parse_arg_eq_done(&pt);
    278             return CMD_USAGE;
    279         }
    280 
    281         /* Make sure local CPU is set */
    282         CPUDB_KEY_COPY(local_key, db_refs[cur_db]->local_entry->base.key);
    283         atptrans_socket_local_cpu_key_set(local_key);
    284         
    285         /* Install socket for given dest CPU */
    286         rv = atptrans_socket_install(dest_key, dest_ip, 0x0);
    287         if (BCM_FAILURE(rv)) {
    288             cli_out("Cannot install socket interface in ATP transport "
    289                     "for CPU key " CPUDB_KEY_FMT_EOLN, 
    290                     CPUDB_KEY_DISP(dest_key));
    291         }
    292         parse_arg_eq_done(&pt);
    293         
    294     } else if (parse_cmp("UNINStall", subcmd, '\0')) {
    295         parse_table_init(unit, &pt);
    296         parse_table_add(&pt, "DestKey", PQ_DFL|PQ_KEY, 0, 
    297                         dest_key.key, 0);
    298         if (parse_arg_eq(args, &pt) <= 0) {
    299             parse_arg_eq_done(&pt);
    300             return CMD_USAGE;
    301         }
    302         rv = atptrans_socket_uninstall(dest_key);
    303         parse_arg_eq_done(&pt);
    304         
    305     } else if (parse_cmp("SERVer", subcmd, '\0')) {
    306         if ((subcmd = ARG_GET(args)) == NULL) {
    307             cli_out("Need option\n");
    308             return CMD_USAGE;
    309         }
    310         if (sal_strcasecmp(subcmd, "START") == 0) {
    311             rv = atptrans_socket_server_start();
    312 
    313         } else if (sal_strcasecmp(subcmd, "STOP") == 0) {
    314             rv = atptrans_socket_server_stop();
    315 
    316         } else {
    317             cli_out("Invalid argument: %s\n", subcmd);
    318             return CMD_USAGE;
    319         }
    320     } else if (parse_cmp("CONFIG", subcmd, '\0')) {
    321    /* Should be called after atptrans_socket_server_start() */
    322         /* Get destination priority and port numbers */
    323         int         priority;
    324         int         listen_port;
    325         int         connect_port;
    326 
    327         parse_table_init(unit, &pt);
    328         parse_table_add(&pt, "PRIority", PQ_DFL | PQ_INT, 0, &priority, 0);
    329         parse_table_add(&pt, "ListenPORT", PQ_DFL | PQ_INT, 0,
    330                         (void *) &listen_port, 0);
    331         parse_table_add(&pt, "ConnectionPORT", PQ_DFL | PQ_INT, 0,
    332                         (void *) &connect_port, 0);
    333         if (parse_arg_eq(args, &pt) <= 0) {
    334             parse_arg_eq_done(&pt);
    335             return CMD_USAGE;
    336         }
    337         rv = atptrans_socket_config_set(priority, listen_port, connect_port);
    338 #ifdef BROADCOM_DEBUG
    339     } else if (parse_cmp("SHOW", subcmd, '\0')) {
    340         atptrans_socket_show();
    341 #endif /* BROADCOM_DEBUG */
    342     } else {
    343         cli_out("Invalid option: %s\n", subcmd);
    344         return CMD_USAGE;
    345     }
    346     if (BCM_FAILURE(rv)) {
    347         if (rv == BCM_E_UNAVAIL) {
    348             cli_out("Socket interface not available for current system\n");
    349         }
    350         else {
    351             cli_out("Command failed\n");
    352         }
    353     }
    354 
    355     return rv;
    356 }
    357 #endif /* INCLUDE_ATPTRANS_SOCKET */
    358 
    359 
    360 cmd_result_t
    361 ct_setup(int unit, args_t *args)
    362 {
    363     parse_table_t  pt;
    364     char *subcmd;
    365     int rv;
    366     int db_idx = cur_db;
    367 
    368     COMPILER_REFERENCE(unit);
    369 
    370     if ((subcmd = ARG_GET(args)) == NULL) {
    371         cli_out("Next Hop is%s running\n",
    372                 next_hop_running() ? "" : " not");
    373         cli_out("ATP is%s running\n",
    374                 atp_running() ? "" : " not");
    375         return CMD_OK;
    376     }
    377 
    378     if (sal_strcasecmp(subcmd, "RX") == 0) {
    379         char *next_arg;
    380 
    381         if ((next_arg = ARG_GET(args)) == NULL) {
    382             return CMD_USAGE;
    383         }
    384         if (sal_strcasecmp(next_arg, "START")) {
    385             return CMD_USAGE;
    386         }
    387         if (pkt_preinit() < 0) {
    388             cli_out("Failed to start BCM RX\n");
    389             return CMD_FAIL;
    390         }
    391         
    392     } else if (sal_strcasecmp(subcmd, "NEXTHOP") == 0) {
    393         int idx;
    394         const cpudb_key_t *key;
    395         int s_unit = 0, s_port = 0, duplex = 0;
    396         int did_print = FALSE;
    397 
    398         if (cputrans_trans_count() == 0) {
    399             cputrans_trans_add(&bcm_trans_ptr);
    400         }
    401 
    402         if (ARG_CUR(args) == NULL) {
    403             for (idx = 0; idx < next_hop_num_ports_get(); idx++) {
    404                 if (next_hop_port_get(idx, &s_unit, &s_port, &duplex)
    405                     == BCM_E_NONE) {
    406                     cli_out("NH idx %d:  Unit %d, Port %d: %sarked duplex.\n",
    407                             idx, s_unit, s_port, duplex ? "M" : "Not m");
    408                 } else {
    409                     cli_out("NH idx %d:  INVALID?\n", idx);
    410                 }
    411                 did_print = TRUE;
    412             }
    413             for (idx = 0; idx < next_hop_max_entries_get(); idx++) {
    414                 key = next_hop_key_get(idx);
    415                 if (key != NULL) {
    416                     cli_out("KEY %x:%x:%x:%x:%x:%x is in NH DB\n",
    417                             (*key).key[0], (*key).key[1], (*key).key[2],
    418                             (*key).key[3], (*key).key[4], (*key).key[5]);
    419                     did_print = TRUE;
    420                 }
    421             }
    422             if (!did_print) {
    423                 cli_out("Next Hop database and ports empty\n");
    424             }
    425         } else {
    426             int thrd_pri = -1;
    427             int rx_pri = -1;
    428             int nhvlan = -1, nhcos = -1, src_mod = -1;
    429             int add_ports = 0;
    430             const cpudb_base_t *base;
    431 
    432             parse_table_init(unit, &pt);
    433             parse_table_add(&pt, "DBidx", PQ_DFL|PQ_INT, 0, &db_idx, 0);
    434             parse_table_add(&pt, "THRDpri", PQ_DFL|PQ_INT, 0, &thrd_pri, 0);
    435             parse_table_add(&pt, "RXpri", PQ_DFL|PQ_INT, 0, &rx_pri, 0);
    436             parse_table_add(&pt, "COS", PQ_DFL|PQ_INT, 0, &nhcos, 0);
    437             parse_table_add(&pt, "VLAN", PQ_DFL|PQ_INT, 0, &nhvlan, 0);
    438             parse_table_add(&pt, "SrcMOD", PQ_DFL|PQ_INT, 0, &src_mod, 0);
    439             parse_table_add(&pt, "Add", PQ_DFL|PQ_INT, 0, &add_ports, 0);
    440             if (parse_arg_eq(args, &pt) < 0) {
    441                 parse_arg_eq_done(&pt);
    442                 return CMD_USAGE;
    443             }
    444             parse_arg_eq_done(&pt);
    445             if (db_idx < 0 || db_idx >= MAX_DBS ||
    446                     db_refs[db_idx] == CPUDB_REF_NULL) {
    447                 cli_out("Bad db index: %d\n", db_idx);
    448                 return CMD_FAIL;
    449             }
    450             if (db_refs[db_idx]->local_entry == NULL) {
    451                 cli_out("No local DB entry in cpudb %d\n", db_idx);
    452                 return CMD_FAIL;
    453             }
    454 
    455             if (nhcos >= 0) {
    456                 next_hop_cos_set(nhcos);
    457             }
    458             if (nhvlan >= 0) {
    459                 next_hop_vlan_set(nhvlan);
    460                 /* Load the next hop mac addr into L2 tables */
    461                 rv = nh_tx_dest_install(TRUE, nhvlan);
    462                 if (rv < 0) {
    463                     cli_out("NH TX dest install failed: %s\n", bcm_errmsg(rv));
    464                     return CMD_FAIL;
    465                 }
    466             }
    467             if (src_mod >= 0) {
    468                 int mod, port;
    469 
    470                 nh_tx_src_get(&mod, &port);
    471                 mod = src_mod;
    472                 nh_tx_src_set(mod, port);
    473                 nh_tx_unknown_modid_set(src_mod);
    474             }
    475             
    476             if (pkt_preinit() < 0) {
    477                 cli_out("NH: Pre-init failed\n");
    478                 return CMD_FAIL;
    479             }
    480             
    481             if (next_hop_running()) {
    482                 cli_out("Next hop is running; stopping: %d\n",
    483                         next_hop_stop());
    484             }
    485             
    486             if (thrd_pri >= 0 || rx_pri >= 0) {
    487                 if (next_hop_config_set(NULL, thrd_pri, rx_pri) < 0) {
    488                     cli_out("Warning: config set failed\n");
    489                 }
    490             }
    491 
    492             base = &db_refs[db_idx]->local_entry->base;
    493             if (add_ports > 0) {
    494                 int i, u, p;
    495                 for (i = 0; i < base->num_stk_ports; i++) {
    496                     bcm_port_encap_config_t mode;
    497                     u = base->stk_ports[i].unit;
    498                     p = base->stk_ports[i].port;
    499 
    500                     rv = bcm_port_encap_config_get(u, p, &mode);
    501                     if (BCM_FAILURE(rv)) {
    502                         continue;
    503                     }
    504                     if (mode.encap == BCM_PORT_ENCAP_IEEE) {
    505                         continue;
    506                     }
    507                     rv = next_hop_port_add(u, p, 1);
    508                     if (rv < 0) {
    509                         cli_out("nexthop add (%d.%d) failed: %s\n",
    510                                 u, p, bcm_errmsg(rv));
    511                     }
    512                 }
    513             }
    514             rv = next_hop_start(base);
    515             if (rv < 0) {
    516                 cli_out("ERROR: next_hop_start failed: %s\n", bcm_errmsg(rv));
    517             }
    518         }
    519         return CMD_OK;
    520 #if defined(BROADCOM_DEBUG)
    521     } else if (sal_strcasecmp(subcmd, "NHSHOW") == 0) {
    522         next_hop_dump();
    523         return CMD_OK;
    524 #endif /* BROADCOM_DEBUG */
    525     } else if (sal_strcasecmp(subcmd, "TXLIMIT") == 0) {
    526         int cos = -1;
    527         int limit = 0;
    528         int rv;
    529 
    530         parse_table_init(unit, &pt);
    531         parse_table_add(&pt, "COS", PQ_DFL|PQ_INT, 0, &cos, 0);
    532         parse_table_add(&pt, "Limit", PQ_DFL|PQ_INT, 0, &limit, 0);
    533 
    534         if (parse_arg_eq(args, &pt) < 0) {
    535             parse_arg_eq_done(&pt);
    536             return CMD_USAGE;
    537         }
    538 
    539         if (cos == -1) {
    540             for (cos = 0; cos <= BCM_COS_MAX; cos++) {
    541                 cputrans_tx_alloc_limit_get(cos, &limit);
    542                 cli_out("Limit for COS %d currently %d\n", cos, limit);
    543             }
    544             parse_arg_eq_done(&pt);
    545             return CMD_OK;
    546         }
    547 
    548         rv = cputrans_tx_alloc_limit_set(cos, limit);
    549         if (rv < 0) {
    550             cli_out("WARNING: limit set returned %d: %s\n", rv, bcm_errmsg(rv));
    551         }
    552 
    553         parse_arg_eq_done(&pt);
    554         return CMD_OK;
    555         
    556     } else if (sal_strcasecmp(subcmd, "ATP") == 0) {
    557         int tx_pri = -1;
    558         int rx_pri = -1;
    559         int atp_cos = -1, atp_vlan = -1;
    560 
    561 #if defined(BROADCOM_DEBUG)
    562         if ((subcmd = ARG_CUR(args)) != NULL) {
    563             if (sal_strcasecmp(subcmd, "SHOW") == 0) {
    564                 ARG_NEXT(args);
    565                 atp_dump(1);
    566                 return CMD_OK;
    567             }
    568         }
    569 #endif /* BROADCOM_DEBUG */
    570 
    571 #ifdef INCLUDE_ATPTRANS_SOCKET        
    572         if ((subcmd = ARG_CUR(args)) != NULL) {
    573             if (parse_cmp("TRANSport", subcmd, '\0')) {
    574                 ARG_NEXT(args);
    575                 if ((subcmd = ARG_GET(args)) == NULL) {
    576                     return CMD_USAGE;
    577                 }
    578                 if (db_idx < 0 || db_idx >= MAX_DBS ||
    579                     db_refs[db_idx] == CPUDB_REF_NULL) {
    580                     cli_out("Bad or empty DB index: %d\n", db_idx);
    581                     return CMD_FAIL;
    582                 }
    583                 if (db_refs[db_idx]->local_entry == NULL) {
    584                     cli_out("No local DB entry in cpudb %d\n", db_idx);
    585                     return CMD_FAIL;
    586                 }
    587                 if (parse_cmp("SOCKet", subcmd, '\0')) {
    588                     return atptrans_socket_subcmd(unit, args, db_refs[db_idx]);
    589                 }
    590                 return CMD_USAGE;
    591             }
    592         }
    593 #endif /* INCLUDE_ATPTRANS_SOCKET */
    594 
    595         if (cputrans_trans_count() == 0) {
    596             cputrans_trans_add(&bcm_trans_ptr);
    597         }
    598 
    599         if (pkt_preinit() < 0) {
    600 #ifdef INCLUDE_ATPTRANS_SOCKET
    601             /* If ATP over sockets is running, then device RX is not
    602                required for ATP */
    603             if (!atptrans_socket_server_running()) {
    604                 cli_out("ATP: Pre-init failed\n");
    605                 return CMD_FAIL;
    606             }
    607 #else
    608             cli_out("ATP: Pre-init failed\n");
    609             return CMD_FAIL;
    610 #endif
    611         }
    612 
    613         parse_table_init(unit, &pt);
    614         parse_table_add(&pt, "DBidx", PQ_DFL|PQ_INT, 0, &db_idx, 0);
    615         parse_table_add(&pt, "TXPrio", PQ_DFL|PQ_INT, 0, &tx_pri, 0);
    616         parse_table_add(&pt, "RXPrio", PQ_DFL|PQ_INT, 0, &rx_pri, 0);
    617         parse_table_add(&pt, "COS", PQ_DFL|PQ_INT, 0, &atp_cos, 0);
    618         parse_table_add(&pt, "VLAN", PQ_DFL|PQ_INT, 0, &atp_vlan, 0);
    619         if (parse_arg_eq(args, &pt) < 0) {
    620             parse_arg_eq_done(&pt);
    621             return CMD_USAGE;
    622         }
    623         if (db_idx < 0 || db_idx >= MAX_DBS ||
    624                 db_refs[db_idx] == CPUDB_REF_NULL) {
    625             cli_out("Bad db index: %d\n", db_idx);
    626             parse_arg_eq_done(&pt);
    627             return CMD_FAIL;
    628         }
    629         if (db_refs[db_idx]->local_entry == NULL) {
    630             cli_out("No local DB entry in cpudb %d\n", db_idx);
    631             parse_arg_eq_done(&pt);
    632             return CMD_FAIL;
    633         }
    634 
    635         atp_cos_vlan_set(atp_cos, atp_vlan);
    636         rv = atp_config_set(tx_pri, rx_pri, NULL);
    637         if (rv < 0) {
    638             cli_out("ERROR: ATP config set failed: %s\n", bcm_errmsg(rv));
    639         }
    640         atp_db_update(db_refs[db_idx]);
    641         rv = atp_start(ATP_F_LEARN_SLF,
    642                        ATP_UNITS_ALL,
    643                        BCM_RCO_F_ALL_COS);
    644         if (rv < 0) {
    645             cli_out("ERROR: ATP start failed: %s\n", bcm_errmsg(rv));
    646         }
    647         parse_arg_eq_done(&pt);
    648 #if defined(BROADCOM_DEBUG)
    649     } else if (sal_strcasecmp(subcmd, "C2C") == 0) {
    650         if ((subcmd = ARG_GET(args)) != NULL) {
    651             if (sal_strcasecmp(subcmd, "SHOW") == 0) {
    652                 c2c_dump();
    653                 return CMD_OK;
    654             }
    655         }
    656 #endif /* BROADCOM_DEBUG */
    657     } else if (sal_strcasecmp(subcmd, "STOP") == 0) {
    658         rv = atp_stop();
    659         if (rv < 0) {
    660             cli_out("ERROR: ATP stop failed: %s\n", bcm_errmsg(rv));
    661         }
    662     } else if (sal_strcasecmp(subcmd, "TIMEOUT") == 0) {
    663         int retry_us = -1, retries = -1;
    664         
    665         if (ARG_CUR(args) == NULL) {
    666             atp_timeout_get(&retry_us, &retries);
    667             cli_out("Retries occur after %d usecs and will be made %d times\n",
    668                     retry_us, retries);
    669         } else {
    670             parse_table_init(unit, &pt);
    671             parse_table_add(&pt, "RetryTO", PQ_DFL|PQ_INT, 0, &retry_us, 0);
    672             parse_table_add(&pt, "RETRIES", PQ_DFL|PQ_INT, 0, &retries, 0);
    673             if (parse_arg_eq(args, &pt) < 0) {
    674                 parse_arg_eq_done(&pt);
    675                 return CMD_USAGE;
    676             }
    677             cli_out("Retry set(%d, %d) returns %d\n", retry_us, retries,
    678                     atp_timeout_set(retry_us, retries));
    679             parse_arg_eq_done(&pt);
    680         }
    681     } else if (sal_strcasecmp(subcmd, "SEGLEN") == 0) {
    682         int seg_len = -1;
    683         char *next_arg;
    684 
    685         if ((next_arg = ARG_GET(args)) == NULL) {
    686             seg_len = atp_segment_len_get();
    687             cli_out("Current segmentation length is %d\n", seg_len);
    688         } else {
    689             seg_len = strtoul(next_arg, NULL, 0);
    690             cli_out("Seg_len set(%d) returns %d\n", seg_len,
    691                     atp_segment_len_set(seg_len));
    692         }
    693     } else if (sal_strcasecmp(subcmd, "POOLSIZE") == 0) {
    694         int tx_pool, rx_pool;
    695         /* Get current pool sizes in case one is not specified */
    696         atp_pool_size_get(&tx_pool, &rx_pool);
    697         if (ARG_CUR(args) == NULL) {
    698             cli_out("Current TX pool: %d.  RX pool %d.\n", tx_pool, rx_pool);
    699         } else {
    700             parse_table_init(unit, &pt);
    701             parse_table_add(&pt, "TX", PQ_DFL|PQ_INT, 0, &tx_pool, 0);
    702             parse_table_add(&pt, "RX", PQ_DFL|PQ_INT, 0, &rx_pool, 0);
    703             if (parse_arg_eq(args, &pt) < 0) {
    704                 parse_arg_eq_done(&pt);
    705                 return CMD_USAGE;
    706             }
    707             cli_out("Pool size set returns %d\n",
    708                     atp_pool_size_set(tx_pool, rx_pool));
    709             parse_arg_eq_done(&pt);
    710         }
    711 #if defined(BCM_RPC_SUPPORT)
    712     } else if (sal_strcasecmp(subcmd, "TUNNEL") == 0) {
    713         if ((subcmd = ARG_GET(args)) == NULL) {
    714             return CMD_USAGE;
    715         }
    716 
    717         if (sal_strcasecmp(subcmd, "mode") == 0) {
    718             int mode = -1;
    719             char *mode_str = NULL;
    720 
    721             if ((mode_str = ARG_GET(args)) == NULL) {
    722 
    723                 /* Display the current rx tunnel mode */
    724                 mode = ct_rx_tunnel_mode_default_get();
    725                 if (BCM_CPU_TUNNEL_PACKET_RELIABLE == mode) {
    726                     mode_str = "RELIABLE";
    727                 } else if (BCM_CPU_TUNNEL_PACKET_BEST_EFFORT == mode) {
    728                     mode_str = "BESTEFFORT";
    729                 }
    730 
    731                 if (NULL != mode_str) {
    732                     cli_out("Default rx tunnel mode is %s.\n", mode_str);
    733                 }
    734             } else {
    735                 if (sal_strcasecmp(mode_str, "BESTEFFORT") == 0) {
    736                     mode = BCM_CPU_TUNNEL_PACKET_BEST_EFFORT;
    737                 } else if (sal_strcasecmp(mode_str, "RELIABLE") == 0) {
    738                     mode = BCM_CPU_TUNNEL_PACKET_RELIABLE;
    739                 } else {
    740                     return CMD_USAGE;
    741                 }
    742 
    743                 rv = ct_rx_tunnel_mode_default_set(mode);
    744                 if (BCM_FAILURE(rv)) {
    745                     return CMD_FAIL;
    746                 }
    747             }
    748         } else if (sal_strcasecmp(subcmd, "set") == 0) {
    749             rv = ct_tx_tunnel_setup();
    750             cli_out("BCM TX Tunnel Setup returns %s\n", bcm_errmsg(rv));
    751         } else if (sal_strcasecmp(subcmd, "destroy") == 0) {
    752             rv = ct_tx_tunnel_stop();
    753             cli_out("BCM TX Tunnel Stop returns %s\n", bcm_errmsg(rv));
    754         } else {
    755             return CMD_USAGE;
    756         }
    757 #endif /* BCM_RPC_SUPPORT */
    758     } else {
    759         cli_out("Subcommand not found: %s\n", subcmd);
    760         return CMD_USAGE;
    761     }
    762 
    763     return CMD_OK;
    764 }
    765 
    766 
    767 static char *cti_driver_list[] = {
    768     "BCM",
    769     "KERNEL",
    770     "KREMOTE",
    771     "OOB",
    772     "NONE",
    773     NULL};
    774 
    775 #define CTI_DRIVER_BCM                 0
    776 #define CTI_DRIVER_KERNEL              1
    777 #define CTI_DRIVER_KREMOTE             2
    778 #define CTI_DRIVER_OOB                 3
    779 #define CTI_DRIVER_NONE                4
    780 
    781 static bcm_trans_ptr_t * cti_driver_ptrs[] = {
    782     &bcm_trans_ptr,   /* Usual BCM */
    783     NULL,             /* Local via kernel driver */
    784     NULL,             /* Remote via kernel driver */
    785     NULL,             /* Out of band driver */
    786     NULL              /* Install NULL driver */
    787 };
    788 
    789 static char *cti_transport_list[] = {"NH", "DBENTRY", "ALL", "DEF", NULL};
    790 
    791 #define CTI_TRANS_NH                 0
    792 #define CTI_TRANS_DBENTRY            1
    793 #define CTI_TRANS_ALL                2
    794 #define CTI_TRANS_DEF                3
    795 
    796 char ct_install_usage[] =
    797     "Usage:  CTInstall [Key=<key> Driver=<mode> Unit=<unit> Port=<port>\n"
    798     "                Transport=<mode> DBidx=<n>]\n"
    799     "    Install per CPU transport driver for the given CPU key\n"
    800 #ifndef COMPILER_STRING_CONST_LIMIT
    801     "    Current drivers supported include:\n"
    802     "        BCM     -  The usual BCM transport pointers\n"
    803     "        KERNEL  -  Local devices via a user to kernel interface\n"
    804     "        KREMOTE -  Remote devices via a user to kernel interface\n"
    805     "        OOB     -  Out of band driver\n"
    806     "        NONE    -  Install NULL driver (disable per-CPU driver)\n"
    807     "    Current transports include:\n"
    808     "        NH      -  Install for next hop transmit\n"
    809     "        DBENTRY -  Install in DB entry which affects CPU2CPU\n"
    810     "        ALL     -  Install in all transports above\n"
    811     "        DEF     -  Install as the default transport for C2C and NH\n"
    812 #endif
    813     ;
    814 
    815 cmd_result_t
    816 ct_install(int unit, args_t *args)
    817 {
    818     parse_table_t pt;
    819     int rv;
    820     int drv_mode = CTI_DRIVER_NONE;
    821     int trans_mode = CTI_TRANS_ALL;
    822     cpudb_key_t key;
    823     int stk_unit;
    824     bcm_port_t stk_port;
    825     int db_idx = -1;
    826 
    827     parse_table_init(unit, &pt);
    828     parse_table_add(&pt, "Driver", PQ_DFL|PQ_MULTI, 0, &drv_mode,
    829                     cti_driver_list);
    830     parse_table_add(&pt, "Transport", PQ_DFL|PQ_MULTI, 0, &trans_mode,
    831                     cti_transport_list);
    832     parse_table_add(&pt, "Key", PQ_DFL|PQ_KEY, 0, key.key, 0);
    833     parse_table_add(&pt, "Unit", PQ_DFL|PQ_INT, 0, &stk_unit, 0);
    834     parse_table_add(&pt, "Port", PQ_DFL|PQ_PORT, 0, &stk_port, 0);
    835     parse_table_add(&pt, "DBidx", PQ_DFL|PQ_INT, 0, &db_idx, 0);
    836 
    837     if (parse_arg_eq(args, &pt) < 0) {
    838         parse_arg_eq_done(&pt);
    839         return CMD_USAGE;
    840     }
    841 
    842 #ifdef INCLUDE_USER_KERNEL_TRANSPORTS
    843     {
    844         static int init = 1; 
    845         if(init == 1) {
    846             {
    847                 static bcm_trans_ptr_t* bcm_uk_trans = NULL; 
    848                 extern int bcm_uk_trans_create(char* service, int port,
    849                                                bcm_trans_ptr_t** p); 
    850                 assert(bcm_uk_trans_create(NULL, stk_port,
    851                                            &bcm_uk_trans) >= 0); 
    852                 cti_driver_ptrs[CTI_DRIVER_KERNEL] = bcm_uk_trans; 
    853                 cputrans_trans_add(bcm_uk_trans); 
    854             }
    855             {
    856                 static bcm_trans_ptr_t* bcm_uk_rtrans = NULL; 
    857                 extern int bcm_uk_rtrans_create(char* service,
    858                                                 bcm_trans_ptr_t** p); 
    859                 assert(bcm_uk_rtrans_create(NULL, &bcm_uk_rtrans) >= 0); 
    860                 if(bcm_uk_rtrans) {
    861                     cti_driver_ptrs[CTI_DRIVER_KREMOTE] = bcm_uk_rtrans; 
    862                     cputrans_trans_add(bcm_uk_rtrans); 
    863                 }
    864             }
    865         }
    866         init = 0; 
    867     }
    868 #endif /* INCLUDE_KERNEL_USER_TRANSPORTS */
    869     
    870     if (drv_mode == CTI_DRIVER_NONE) {
    871         cli_out("Note:  Clearing per CPU transport with NULL driver\n");
    872     } else if (cti_driver_ptrs[drv_mode] == NULL) {
    873         cli_out("Sorry, driver for %s is not yet implemented\n",
    874                 cti_driver_list[drv_mode]);
    875         return CMD_FAIL;
    876     }
    877 
    878 
    879     /* Handle default transport request */
    880     if(trans_mode == CTI_TRANS_DEF) {
    881         /* Install this transport as the default for both C2C and NH */
    882         bcm_trans_ptr_t* tp;
    883         bcm_mac_t l; 
    884         uint32 flags; 
    885         
    886         c2c_config_get(&tp, l, &flags); 
    887         tp = cti_driver_ptrs[drv_mode];
    888         /* Enable checking for destination MAC in C2C packets */
    889         flags |= C2C_F_DEST_MAC_CHECK_ENABLE;
    890         c2c_config_set(tp, l, flags); 
    891         nh_tx_setup(tp); 
    892     }
    893 
    894     /* Handle next hop transport request */
    895     if (trans_mode == CTI_TRANS_ALL || trans_mode == CTI_TRANS_NH) {
    896         rv = nh_tx_trans_ptr_set(stk_unit, stk_port,
    897                                  cti_driver_ptrs[drv_mode]);
    898 
    899         if (rv < 0) {
    900             cli_out("WARNING:  nh_tx pointer set returned %d: %s\nContinuing\n",
    901                     rv, bcm_errmsg(rv));
    902         }
    903     }
    904 
    905     /* Handle C2C transport request */
    906     if (trans_mode == CTI_TRANS_ALL || trans_mode == CTI_TRANS_DBENTRY) {
    907         cpudb_entry_t *entry;
    908 
    909         if (db_idx == -1) {
    910             db_idx = cur_db;
    911         }
    912         if (db_refs[db_idx] == CPUDB_REF_NULL) {
    913             cli_out("DB %d is not setup.  Try 'CPUDB create'.\n", db_idx);
    914             return CMD_FAIL;
    915         }
    916         CPUDB_KEY_SEARCH(db_refs[db_idx], key, entry);
    917         if (entry == NULL) {
    918             cli_out("ERROR: DB entry not found\n");
    919             return CMD_FAIL;
    920         }
    921         entry->trans_ptr = cti_driver_ptrs[drv_mode];
    922     }
    923 
    924     return CMD_OK;
    925 }
    926 
    927 
    928 bcm_rx_t ab_echo_cb(cpudb_key_t src_key,
    929                     int client_id,
    930                     bcm_pkt_t *pkt,
    931                     uint8 *payload,
    932                     int payload_len,
    933                     void *cookie);
    934 
    935 char echo_setup_usage[] =
    936     "CTEchoSetup <subcmd>\n"
    937 ;
    938 
    939 char ct_echo_usage[] =
    940     "CTEcho <subcmd> [opts]\n"
    941     "    REG [Mode=<mode>] [DBidx=<n>] [Reassem=<T|F>]\n"
    942     "        Register for the given mode, with the given CPUDB\n"
    943     "    UNREG [Mode=<mode>]"
    944     "        Unregister for the given mode\n"
    945     "    ECHO String=<string> [Mode=<ATP|BET|ATPNH|BETNH|BETBcast>] \n"
    946     "        [DEPTH=<n>] [DestKey=<key>] [ALLOChdr=<T|F>] [ASYNC=<T|F>]\n"
    947     "        [MinLen=<n>] [Regen=<T|F>]\n"
    948     "    Echo a string, with recursive calls to the given depth.\n";
    949 
    950 
    951 #define PACK_SHORT(buf, val) \
    952     do {                                               \
    953         uint16 v2;                                     \
    954         v2 = bcm_htons(val);                           \
    955         sal_memcpy(buf, &v2, sizeof(uint16));          \
    956     } while (0)
    957 
    958 #define UNPACK_SHORT(buf, val) \
    959     do {                                               \
    960         sal_memcpy(&(val), buf, sizeof(uint16));       \
    961         val = bcm_ntohs(val);                          \
    962     } while (0)
    963 
    964 #define PACK_LONG(buf, val) \
    965     do {                                               \
    966         uint32 v2;                                     \
    967         v2 = bcm_htonl(val);                           \
    968         sal_memcpy(buf, &v2, sizeof(uint32));           \
    969     } while (0)
    970 
    971 #define UNPACK_LONG(buf, val) \
    972     do {                                               \
    973         sal_memcpy(&(val), buf, sizeof(uint32));         \
    974         val = bcm_ntohl(val);                          \
    975     } while (0)
    976 
    977 
    978 /* First, a handler for echo packets */
    979 
    980 typedef struct {
    981     int verbose;
    982 } CallbackOptions;
    983 
    984 
    985 /* Cookie data for echo async callback */
    986 typedef struct {
    987     int    unit;
    988 } _echo_cb_async_t;
    989 
    990 #define ECHO_BASE_CLI_ID   1000
    991 #define CTE_MODE_ATP                 0
    992 #define CTE_MODE_BET                 1
    993 #define CTE_MODE_ATP_NH              2
    994 #define CTE_MODE_BET_NH              3
    995 #define CTE_MODE_BET_BCAST           4
    996 #define CTE_MODE_COUNT               5
    997 
    998 #define CTE_MODE_MASK                0xf
    999 #define CTE_FLAGS_ASYNC_FREE         0x100
   1000 #define CTE_FLAGS_ALLOC_HDR_HERE     0x200
   1001 #define CTE_FLAGS_IS_BCAST           0x400
   1002 #define CTE_FLAGS_VERBOSE            0x800
   1003 #define CTE_FLAGS_CRC_REGEN          0x1000
   1004 
   1005 static char *cte_mode_list[] = {"ATP", "BET", "ATPNH", "BETNH", "BETBcast",
   1006                                 NULL};
   1007 
   1008 static CallbackOptions cb_options[CTE_MODE_COUNT];
   1009 
   1010 STATIC void echo_free_buf(void *payload_ptr, void *pkt_ptr, void *p3,
   1011                           void *p4, void *p5)
   1012 {
   1013     atp_rx_free(payload_ptr, pkt_ptr);
   1014 }
   1015 
   1016 STATIC void
   1017 _send_echo_pkt_cb(uint8 *pkt, void *cookie, int rv)
   1018 {
   1019     _echo_cb_async_t *async_data;
   1020 
   1021     if (cookie == NULL) {
   1022         cli_out("Error in async callback routine; missing data\n");
   1023         return;
   1024     }
   1025     
   1026     async_data = (_echo_cb_async_t *)cookie;
   1027     cli_out("Async callback routine, unit %d\n", async_data->unit);
   1028 
   1029     /* Free buffer allocated earlier in _ct_echo() */
   1030     sal_dma_free(pkt);
   1031     sal_free(async_data);
   1032 }
   1033 
   1034 STATIC int
   1035 _send_echo_pkt(int client_id,
   1036                uint8 *payload,
   1037                int payload_len,
   1038                int depth,
   1039                uint32 pkt_flags,
   1040                int mode,
   1041                int verbose,
   1042                cpudb_key_t dest_key,
   1043                void *cookie)
   1044 {
   1045     int payload_offset = 0;
   1046     uint32 ct_flags = 0;
   1047     atp_tx_cb_f cb = NULL;
   1048     int crc_payload_len;
   1049     int rv;
   1050 
   1051     if (verbose) {
   1052         cli_out("echo pkt: %d bytes to " CPUDB_KEY_FMT_EOLN, payload_len,
   1053                 CPUDB_KEY_DISP(dest_key));
   1054     }
   1055     PACK_SHORT(payload, depth);
   1056     PACK_LONG(&payload[sizeof(uint16)], pkt_flags);
   1057 
   1058     /* There are potentially two CRCs involved here: one is the CRC
   1059        that protects the ECHO protocol payload, which is always
   1060        present. The other is the Ethernet FCS, which is calculated by
   1061        the Ethernet transmitter, space for which is only allocated if
   1062        CTE_FLAGS_CRC_REGEN is set. */
   1063     crc_payload_len = payload_len - sizeof(uint32);
   1064     if (pkt_flags & CTE_FLAGS_CRC_REGEN) {
   1065         crc_payload_len -= sizeof(uint32);
   1066     }
   1067 
   1068     PACK_LONG(payload+crc_payload_len,
   1069               ~_shr_crc32(~0, payload, crc_payload_len));
   1070     
   1071     if (mode == CTE_MODE_BET_BCAST) {
   1072         ct_flags = CPUTRANS_BCAST;
   1073     }
   1074 
   1075     if (pkt_flags & CTE_FLAGS_ALLOC_HDR_HERE) {
   1076         payload_offset = CPUTRANS_HEADER_BYTES;
   1077         ct_flags |= CPUTRANS_NO_HEADER_ALLOC;
   1078     }
   1079 
   1080     if (pkt_flags & CTE_FLAGS_CRC_REGEN) {
   1081         ct_flags |= CPUTRANS_CRC_REGEN;
   1082     }
   1083 
   1084     if (pkt_flags & CTE_FLAGS_ASYNC_FREE) {
   1085         cb = _send_echo_pkt_cb;
   1086     }
   1087 
   1088     rv = atp_tx(dest_key,
   1089                     client_id,
   1090                     payload - payload_offset,
   1091                     payload_len + payload_offset,
   1092                     ct_flags,
   1093                     cb,
   1094                     cookie);
   1095 
   1096     return rv;
   1097 }
   1098 
   1099 
   1100 bcm_rx_t
   1101 ab_echo_cb(cpudb_key_t src_key,
   1102            int client_id,
   1103            bcm_pkt_t *pkt,
   1104            uint8 *payload,
   1105            int payload_len,
   1106            void *cookie)
   1107 {
   1108     uint16 depth;
   1109     int async_free;
   1110     uint32 pkt_flags;
   1111     bcm_rx_t rv = BCM_RX_HANDLED;
   1112     int offset = 0;
   1113     int mode;
   1114     int len;
   1115     CallbackOptions *options = (CallbackOptions *)cookie;
   1116 
   1117     if (payload == NULL) {  /* Just use first segment for string */
   1118         if (pkt == NULL) {
   1119             cli_out("CT echo error: payload and pkt both NULL, cli %d\n",
   1120                     client_id);
   1121             return BCM_RX_NOT_HANDLED;
   1122         }
   1123         payload = pkt->pkt_data[1].data;
   1124         len = pkt->pkt_data[1].len;
   1125         cli_out("CT echo warning: \n");
   1126         cli_out("   Segmented packet; segmentation not maintained on echo\n");
   1127     } else {
   1128         len = payload_len;
   1129     }
   1130 
   1131     UNPACK_SHORT(payload, depth);
   1132     offset += sizeof(uint16);
   1133     UNPACK_LONG(payload + offset, pkt_flags);
   1134     offset += sizeof(uint32);
   1135     async_free = pkt_flags & CTE_FLAGS_ASYNC_FREE;
   1136     mode = client_id - ECHO_BASE_CLI_ID;
   1137 
   1138     if (mode < 0 || mode > 4) {
   1139         cli_out("CT echo error: Bad client id: %d\n", client_id);
   1140         return BCM_RX_NOT_HANDLED;
   1141     }
   1142 
   1143     if (sal_strlen((char *)&payload[offset]) > len) {
   1144         cli_out("CT echo error: Unterminated string in first segment\n");
   1145         return BCM_RX_HANDLED;
   1146     }
   1147 
   1148     if (_shr_crc32(~0, payload, len) != _SHR_CRC32_CORRECT) {
   1149         cli_out("CT echo error: Echo payload CRC failure.\n");
   1150     }
   1151     
   1152     if (options->verbose) {
   1153         cli_out("%s echo request from " CPUDB_KEY_FMT ": %s\n",
   1154                 cte_mode_list[mode],
   1155                 CPUDB_KEY_DISP(src_key),
   1156                 &payload[offset]);
   1157         cli_out("       Depth %d, flags 0x%x, len %d. (total %d)\n",
   1158                 depth, pkt_flags, len - offset, payload_len);
   1159     }
   1160 
   1161     if (depth > 0) {
   1162         /* Return echo: one less depth, clear async flag */
   1163         pkt_flags &= ~CTE_FLAGS_ASYNC_FREE;
   1164         pkt_flags &= ~CTE_FLAGS_CRC_REGEN;
   1165         _send_echo_pkt(client_id, payload, len, depth - 1,
   1166                        pkt_flags, mode, options->verbose, src_key, NULL);
   1167     }
   1168 
   1169     if (async_free) {
   1170         rv = BCM_RX_HANDLED_OWNED;
   1171         sal_dpc(echo_free_buf, payload, pkt, NULL, NULL, NULL);
   1172     } else {
   1173         rv = BCM_RX_HANDLED;
   1174     }
   1175 
   1176     return rv;
   1177 }
   1178 
   1179 #define ECHO_STR_OFFSET (sizeof(uint16) + sizeof(uint32))
   1180 
   1181 typedef struct {
   1182     uint8 *pkt_buf;
   1183     uint32 pkt_flags;
   1184     uint8 *payload_buf;
   1185     int payload_len;
   1186 } _echo_payload_t;
   1187 
   1188 int
   1189 _ct_echo_pkt_create(_echo_payload_t *ep,
   1190                     char *str, int mode, int cte_flags, int minlen)
   1191 {
   1192     int payload_len, len, len_str;
   1193     uint32 pkt_flags = 0;
   1194     uint8 *pkt_buf, *payload_buf;
   1195     int alloc_hdr_here = cte_flags & CTE_FLAGS_ALLOC_HDR_HERE;
   1196 
   1197     sal_memset(ep, 0, sizeof(*ep));
   1198 
   1199     if (str == NULL) {
   1200         cli_out("Null string\n");
   1201         return 0;
   1202     }
   1203 
   1204     pkt_flags = mode;
   1205     len_str = sal_strlen(str);
   1206     len = len_str + 1;
   1207     len += ECHO_STR_OFFSET;      /* To store depth, flags */
   1208     len += sizeof(uint32);       /* CRC at end of payload */
   1209     
   1210     if (cte_flags & CTE_FLAGS_CRC_REGEN) {
   1211         pkt_flags |= CTE_FLAGS_CRC_REGEN;
   1212         len += sizeof(uint32);       /* CRC at end of Ethernet frame */
   1213     }
   1214     
   1215     len = minlen > len ? minlen : len;  /* Take min length if set */
   1216     payload_len = len;
   1217     if (alloc_hdr_here) {
   1218         len += CPUTRANS_HEADER_BYTES;
   1219         pkt_flags |= CTE_FLAGS_ALLOC_HDR_HERE;
   1220     }
   1221 
   1222     /* Create packet */
   1223     pkt_buf = sal_dma_alloc(len, "ct_echo");
   1224     if (pkt_buf == NULL) {
   1225         cli_out("Could not alloc packet buffer\n");
   1226         return 0;
   1227     }
   1228 
   1229     /* Pack the string into the payload past depth, flags */
   1230     payload_buf = pkt_buf;
   1231     if (alloc_hdr_here) {
   1232         payload_buf += CPUTRANS_HEADER_BYTES;
   1233     }
   1234 
   1235     sal_strncpy((char *)&payload_buf[ECHO_STR_OFFSET], str, len_str);
   1236     if (len_str)
   1237         *(char*)&payload_buf[ECHO_STR_OFFSET+len_str] = '\0';
   1238 
   1239     ep->pkt_buf = pkt_buf;
   1240     ep->pkt_flags = pkt_flags;
   1241     ep->payload_buf = payload_buf;
   1242     ep->payload_len = payload_len;
   1243 
   1244     return 1;
   1245 }
   1246 
   1247 
   1248 void
   1249 _ct_echo_async(int unit, cpudb_key_t dest_key,
   1250                char *str, int depth, int mode,
   1251                int cte_flags, int minlen)
   1252 {
   1253     void *cookie = NULL;
   1254     _echo_cb_async_t  *async_data = NULL;
   1255     _echo_payload_t payload;
   1256     int rv = BCM_E_NONE;
   1257     int verbose = cte_flags & CTE_FLAGS_VERBOSE;
   1258 
   1259     if (!_ct_echo_pkt_create(&payload, str, mode, cte_flags, minlen)) {
   1260         return;
   1261     }
   1262     payload.pkt_flags |= CTE_FLAGS_ASYNC_FREE;
   1263 
   1264     if ((async_data = sal_alloc(sizeof(*async_data), "ct-echo")) == NULL) {
   1265         cli_out("Could not alloc memory for async data\n");
   1266         return;
   1267     }
   1268     async_data->unit = unit;
   1269     cookie = (void *)async_data;
   1270 
   1271     if (verbose) {
   1272         cli_out("CT echo:  Sending out %s echo request to CPU key "
   1273                 CPUDB_KEY_FMT ", depth %d, flags 0x%x\n",
   1274                 cte_mode_list[mode], CPUDB_KEY_DISP(dest_key),
   1275                 depth, payload.pkt_flags);
   1276     }
   1277 
   1278     rv = _send_echo_pkt(ECHO_BASE_CLI_ID + mode,
   1279                         payload.payload_buf, payload.payload_len,
   1280                         depth, payload.pkt_flags, mode, verbose, dest_key,
   1281                         cookie);
   1282     
   1283     /*
   1284      * Free buffers if atp tx was not successful.  When atp tx is
   1285      * successful , buffers are freed by the callback routine.
   1286      */
   1287     if (BCM_FAILURE(rv)) {
   1288         cli_out("%s tx returns %d (%s)\n", cte_mode_list[mode],
   1289                 rv, bcm_errmsg(rv));
   1290         sal_dma_free(payload.pkt_buf);
   1291         sal_free(async_data);
   1292     } else {
   1293         cli_out("Echo sent successfully\n");
   1294     }
   1295 }
   1296 
   1297 void
   1298 _ct_echo_sync(int unit, cpudb_key_t dest_key,
   1299               char *str, int depth, int mode,
   1300               int cte_flags, int minlen, int count)
   1301 {
   1302     void *cookie = NULL;
   1303     _echo_payload_t payload;
   1304     int rv = BCM_E_NONE;
   1305     int actual = 0;
   1306     int verbose = cte_flags & CTE_FLAGS_VERBOSE;
   1307 
   1308     if (count == 0) {
   1309         cli_out("Nothing to send\n");
   1310         return;
   1311     }
   1312 
   1313     if (!_ct_echo_pkt_create(&payload, str, mode, cte_flags, minlen)) {
   1314         return;
   1315     }
   1316 
   1317     if (verbose) {
   1318         cli_out("CT echo:  Sending out %s echo request to CPU key "
   1319                 CPUDB_KEY_FMT ", depth %d, flags 0x%x\n",
   1320                 cte_mode_list[mode], CPUDB_KEY_DISP(dest_key),
   1321                 depth, payload.pkt_flags);
   1322     }
   1323 
   1324     while (count-- > 0) {
   1325         rv = _send_echo_pkt(ECHO_BASE_CLI_ID + mode,
   1326                             payload.payload_buf, payload.payload_len,
   1327                             depth, payload.pkt_flags, mode, verbose, dest_key,
   1328                             cookie);
   1329 
   1330         if (BCM_FAILURE(rv)) {
   1331             cli_out("%s tx returns %d (%s)\n", cte_mode_list[mode],
   1332                     rv, bcm_errmsg(rv));
   1333             break;
   1334         } else {
   1335             actual++;
   1336         }
   1337     }
   1338 
   1339     if (actual > 0) {
   1340         cli_out("Echo sent successfully\n");
   1341     }
   1342     
   1343     /*
   1344      * Free buffers.
   1345      */
   1346 
   1347     sal_dma_free(payload.pkt_buf);
   1348 
   1349 }
   1350 
   1351 cmd_result_t
   1352 ct_echo(int unit, args_t *args)
   1353 {
   1354     parse_table_t  pt;
   1355     int depth = 1;
   1356     char *str = NULL;
   1357     static cpudb_key_t dest_key;
   1358     int alloc_hdr = FALSE;
   1359     int async = FALSE;
   1360     int mode = 0;
   1361     char *subcmd;
   1362     static int db_idx = -1;
   1363     int rv;
   1364     int flags = 0;
   1365     int minlen = 0;
   1366     int reassem = TRUE;
   1367     int verbose = TRUE;
   1368     int count = 1;
   1369     int crc_regen = FALSE;
   1370     int cte_flags = 0;
   1371 
   1372     if ((subcmd = ARG_GET(args)) == NULL) {
   1373         cli_out("Requires string argument\n");
   1374         return CMD_USAGE;
   1375     }
   1376     if (sal_strcasecmp(subcmd, "REG") == 0) {
   1377         parse_table_init(unit, &pt);
   1378         parse_table_add(&pt, "Mode", PQ_DFL|PQ_MULTI,0, &mode, cte_mode_list);
   1379         parse_table_add(&pt, "DBidx", PQ_DFL|PQ_INT, 0, &db_idx, 0);
   1380         parse_table_add(&pt, "Reassem", PQ_DFL|PQ_BOOL, 0, &reassem, 0);
   1381         parse_table_add(&pt, "Verbose", PQ_DFL|PQ_BOOL, 0, &verbose, 0);
   1382         if (parse_arg_eq(args, &pt) < 0) {
   1383             parse_arg_eq_done(&pt);
   1384             return CMD_USAGE;
   1385         }
   1386         if (db_idx == -1) {
   1387             db_idx = cur_db;
   1388         }
   1389         if (db_refs[db_idx] == CPUDB_REF_NULL) {
   1390             cli_out("DB %d is not setup.  Try 'CPUDB create'.\n", db_idx);
   1391             return CMD_FAIL;
   1392         }
   1393 
   1394         if (reassem) {
   1395             flags |= ATP_F_REASSEM_BUF;
   1396         }
   1397         /* Set next-hop and BET flags appropriately */
   1398         if (mode == CTE_MODE_ATP_NH ||
   1399             mode == CTE_MODE_BET_NH ||
   1400             mode == CTE_MODE_BET_BCAST) {
   1401             flags |= ATP_F_NEXT_HOP;
   1402         }
   1403         if (mode == CTE_MODE_BET_NH ||
   1404             mode == CTE_MODE_BET ||
   1405             mode == CTE_MODE_BET_BCAST) {
   1406             flags |= ATP_F_NO_ACK;
   1407         }
   1408 
   1409         cb_options[mode].verbose = verbose;
   1410         rv = atp_register(ECHO_BASE_CLI_ID + mode, flags, ab_echo_cb,
   1411                               (void *)(cb_options + mode), -1, -1);
   1412         parse_arg_eq_done(&pt);
   1413         if (rv < 0) {
   1414             cli_out("Could not register echo as %s (%d) client (%d): %s\n",
   1415                     cte_mode_list[mode], mode, rv, bcm_errmsg(rv));
   1416             return CMD_FAIL;
   1417         }
   1418     } else if (sal_strcasecmp(subcmd, "UNREG") == 0) {
   1419         parse_table_init(unit, &pt);
   1420         parse_table_add(&pt, "Mode", PQ_DFL|PQ_MULTI,0, &mode, cte_mode_list);
   1421         if (parse_arg_eq(args, &pt) < 0) {
   1422             parse_arg_eq_done(&pt);
   1423             return CMD_USAGE;
   1424         }
   1425         atp_unregister(ECHO_BASE_CLI_ID + mode);
   1426         parse_arg_eq_done(&pt);
   1427     } else if (sal_strcasecmp(subcmd, "ECHO") == 0) {
   1428         parse_table_init(unit, &pt);
   1429         parse_table_add(&pt, "String", PQ_DFL|PQ_STRING, 0, &str, 0);
   1430         parse_table_add(&pt, "MinLen", PQ_DFL|PQ_INT, 0, &minlen, 0);
   1431         parse_table_add(&pt, "DEPTH", PQ_DFL|PQ_INT, 0, &depth, 0);
   1432         parse_table_add(&pt, "Mode", PQ_DFL|PQ_MULTI,0, &mode, cte_mode_list);
   1433         parse_table_add(&pt, "DestKey", PQ_DFL|PQ_KEY, 0, dest_key.key, 0);
   1434         parse_table_add(&pt, "ALLOChdr", PQ_DFL|PQ_BOOL, 0, &alloc_hdr, 0);
   1435         parse_table_add(&pt, "ASYNC", PQ_DFL|PQ_BOOL, 0, &async, 0);
   1436         parse_table_add(&pt, "Verbose", PQ_DFL|PQ_BOOL, 0, &verbose, 0);
   1437         parse_table_add(&pt, "Regen", PQ_DFL|PQ_BOOL, 0, &crc_regen, 0);
   1438         parse_table_add(&pt, "Count", PQ_DFL|PQ_INT, 0, &count, 0);
   1439         if (parse_arg_eq(args, &pt) < 0) {
   1440             parse_arg_eq_done(&pt);
   1441             return CMD_USAGE;
   1442         }
   1443 
   1444         if (alloc_hdr) {
   1445             cte_flags |= CTE_FLAGS_ALLOC_HDR_HERE;
   1446         }
   1447 
   1448         if (verbose) {
   1449             cte_flags |= CTE_FLAGS_VERBOSE;
   1450         }
   1451 
   1452         if (crc_regen) {
   1453             cte_flags |= CTE_FLAGS_CRC_REGEN;
   1454         }
   1455 
   1456         if (async) {
   1457             _ct_echo_async(unit, dest_key, str, depth,
   1458                           mode, cte_flags, minlen);
   1459         } else {
   1460             _ct_echo_sync(unit, dest_key, str, depth,
   1461                           mode, cte_flags, minlen, count);
   1462         }
   1463         parse_arg_eq_done(&pt);
   1464     } else if (sal_strcasecmp(subcmd, "SPIN") == 0) {
   1465         parse_table_init(unit, &pt);
   1466         parse_table_add(&pt, "Count", PQ_DFL|PQ_INT, 0, &count, 0);
   1467         if (parse_arg_eq(args, &pt) < 0) {
   1468             parse_arg_eq_done(&pt);
   1469             return CMD_USAGE;
   1470         }
   1471         while(count-->0);
   1472         parse_arg_eq_done(&pt);
   1473     } else {
   1474         cli_out("Subcommand not found: %s\n", subcmd);
   1475         return CMD_USAGE;
   1476     }
   1477 
   1478 
   1479 
   1480     return CMD_OK;
   1481 }
   1482 
   1483 #endif /* TKS libs included */