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

topo_pkt.c (33306B)


      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:        topo_pkt.c
      8  * Purpose:     All functions related to topo packet generation and
      9  *              handling.
     10  *
     11  * This file contains code related to topology packet generation
     12  * and handling.  It provides default functions that are used by
     13  * stack task if customer applications do not provide alternatives.
     14  * In addition, it provides some building blocks that customers
     15  * may integrate with their own code.
     16  *
     17  * Here's how the sample topology stack task code works:
     18  *
     19  *     1.  stk task enters DISC mode:  Application should call
     20  *         topo_pkt_expect_set(TRUE) to indicate topo pkts
     21  *         should be expected.
     22  *
     23  *     2.  stk task enters TOPO mode:  Stack task calls
     24  *         bcm_stack_topo_update (as programmed into st_config).
     25  *
     26  *     3.  topology packet is received:
     27  *         If both 1 and 2 have occurred, process packet.
     28  *         If only 1 has occurred, bank packet and wait for (2).
     29  *         If only 2 has occurred, it's an error.
     30  *
     31  * If stack task exits TOPO mode, or whenever it goes into blocked
     32  * mode, the application should call topo_pkt_expect_set(FALSE).
     33  */
     34 
     35 #include <shared/bsl.h>
     36 
     37 #include <sdk_config.h>
     38 #include <shared/alloc.h>
     39 
     40 #include <bcm/error.h>
     41 #include <bcm/types.h>
     42 #include <bcm/port.h>
     43 #include <bcm/stack.h>
     44 
     45 #include <appl/cpudb/cpudb.h>
     46 
     47 #include <appl/cputrans/cputrans.h>
     48 #include <appl/cputrans/atp.h>
     49 #include <appl/cputrans/nh_tx.h>
     50 
     51 #include <appl/stktask/stktask.h>
     52 #include <appl/stktask/topology.h>
     53 #include <appl/stktask/topo_pkt.h>
     54 #include <appl/stktask/topo_brd.h>
     55 
     56 #include "topo_int.h"
     57 #include "brd_chassis_int.h"
     58 
     59 static sal_mutex_t st_topo_lock;   /* Serves as init done indication */
     60 static volatile int topo_enabled;  /* Accept topo packets; control w/ expect */
     61 
     62 static cpudb_ref_t topo_current_db = CPUDB_REF_NULL;
     63 static uint8 topo_pkt_buf[TOPO_PKT_BYTES_MAX];
     64 static int topo_pkt_len; /* Also signals pkt is present */
     65 
     66 #define TOPO_PKT_CLEAR         topo_pkt_len = 0
     67 #define TOPO_PKT_SET(_pkt, _len, _src_key)   do {               \
     68         sal_memcpy(topo_pkt_buf, (_pkt), (_len));               \
     69         topo_pkt_len = (_len);                                  \
     70     } while (0)
     71 #define TOPO_PKT_PRESENT       (topo_pkt_len > 0)
     72 
     73 #define ST_TOPO_LOCK sal_mutex_take(st_topo_lock, sal_mutex_FOREVER)
     74 #define ST_TOPO_UNLOCK sal_mutex_give(st_topo_lock)
     75 
     76 /* Process topo pkts when current DB is non NULL */
     77 #define TOPO_ACTIVE (topo_current_db != CPUDB_REF_NULL)
     78 
     79 #ifndef TOPO_THREAD_STACK
     80 #define TOPO_THREAD_STACK       SAL_THREAD_STKSZ
     81 #endif
     82 #ifndef TOPO_THREAD_PRIO
     83 #define TOPO_THREAD_PRIO        50
     84 #endif
     85 
     86 
     87 #define TOPO_DEFERRED 1
     88 
     89 /****************************************************************
     90  *
     91  * Default topology update callback function
     92  */
     93 
     94 STATIC void topo_pkt_process(void);
     95 STATIC int topo_pkt_send(cpudb_ref_t db_ref, cpudb_entry_t *entry);
     96 STATIC int topo_pkt_send_depth_first(cpudb_ref_t db_ref, cpudb_entry_t *entry);
     97 
     98 int
     99 bcm_stack_topo_update(cpudb_ref_t db_ref)
    100 {
    101     int rv = BCM_E_NONE;
    102     cpudb_entry_t      *entry;
    103 
    104     if (st_topo_lock == NULL) { /* Not initialized */
    105         return BCM_E_INIT;
    106     }
    107 
    108     ST_TOPO_LOCK;
    109     if (!topo_enabled) { /* Not enabled */
    110         ST_TOPO_UNLOCK;
    111         return BCM_E_INIT;
    112     }
    113     if (TOPO_ACTIVE) {
    114         ST_TOPO_UNLOCK;
    115         LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    116                  (BSL_META("TOPO: Activated twice\n")));
    117         return BCM_E_FAIL;
    118     }
    119 
    120     topo_current_db = db_ref;
    121 
    122 #if !defined(TOPO_PROCESSING_LOCAL) /* Send out topo info from master */
    123     if (db_ref->local_entry == db_ref->master_entry) { /* Local is master */
    124         /* Generate the topology and send out topo packets */
    125         if ((rv = topology_mod_ids_assign(db_ref)) < 0) {
    126             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    127                       (BSL_META("Could not assign all mod ids: %s\n"),
    128                        bcm_errmsg(rv)));
    129         } else if ((rv = topology_create(db_ref)) < 0) {
    130             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    131                       (BSL_META("Failed to create topology: %s\n"),
    132                        bcm_errmsg(rv)));
    133         } else if ((rv = topo_pkt_send_depth_first(db_ref, 
    134                                             db_ref->master_entry)) < 0) {
    135             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    136                       (BSL_META("Failed to send topo pkt: %s\n"),
    137                        bcm_errmsg(rv)));
    138         }
    139 
    140         /*
    141          * Clear topo visited flags for all cpus
    142          */
    143         CPUDB_FOREACH_ENTRY(db_ref, entry) {
    144             CPUDB_TOPO_NOT_VISITED_SET(entry);
    145         }
    146     } else { /* Not master */
    147         if (TOPO_PKT_PRESENT) { /* Ready to process packet */
    148             topo_pkt_process();
    149         }
    150     }
    151 #else
    152     /* To do:  Implement local topology processing */
    153 #endif
    154 
    155     /* Check rv == BCM_E_NONE before doing anything else */
    156     ST_TOPO_UNLOCK;
    157 
    158     return rv;
    159 }
    160 
    161 STATIC int
    162 _topo_handle_init(void)
    163 {
    164     if (st_topo_lock == NULL) {
    165         st_topo_lock = sal_mutex_create("topo_handle");
    166         if (st_topo_lock == NULL) {
    167             return BCM_E_MEMORY;
    168         }
    169     }
    170 
    171     return BCM_E_NONE;
    172 }
    173 
    174 /****************************************************************
    175  *
    176  * Application to topology interface API functions
    177  *
    178  *     topo_pkt_handle_init:
    179  *         Register w/ ATP to get topo pkts.
    180  *     topo_pkt_expect_set(int enable)
    181  *         Indicate if topology packet should be expected and
    182  *         processed.
    183  *
    184  *     topo_stack_topo_update
    185  *         Normally called by stack task on transition to
    186  *         TOPO state.
    187  */
    188 
    189 STATIC bcm_rx_t topo_pkt_handler(cpudb_key_t src_key,
    190                                  int client_id,
    191                                  bcm_pkt_t *pkt,
    192                                  uint8 *payload,
    193                                  int payload_len,
    194                                  void *cookie);
    195 
    196 /*
    197  * (Re-)Initialize the topo packet handler subsystem.
    198  * Creates lock and registers with ATP with given flags
    199  */
    200 
    201 int
    202 topo_pkt_handle_init(uint32 topo_atp_flags)
    203 {
    204     int rv = BCM_E_NONE;
    205 
    206     if (st_topo_lock == NULL) {
    207         rv = _topo_handle_init();
    208         if (rv < 0) {
    209             return rv;
    210         }
    211     }
    212 
    213     /* Handle ATP-NH registration to receive topo packets from ATP */
    214     atp_unregister(BCM_ST_TOPO_CLIENT_ID);
    215     rv = atp_register(BCM_ST_TOPO_CLIENT_ID, topo_atp_flags,
    216                       topo_pkt_handler, NULL, -1, -1);
    217 
    218     return rv;
    219 }
    220 
    221 /*
    222  * Indicate to topo processing whether to expect topo pkts.
    223  * This should be enabled at the transition into DISC.  It
    224  * should be disabled at the transition to BLOCKED.
    225  *
    226  * This will always clear the local DB pointer.  That pointer
    227  * is set when stack task calls with "start topo processing".
    228  * It must not be changed by stack task until after
    229  * topo_pkt_handle_enable_set(FALSE) is called.
    230  */
    231 
    232 int
    233 topo_pkt_expect_set(int enable)
    234 {
    235     int rv = BCM_E_NONE;
    236 
    237     if (st_topo_lock == NULL) {
    238         return BCM_E_INIT;
    239     }
    240 
    241     ST_TOPO_LOCK;
    242     topo_enabled = enable;
    243     topo_current_db = CPUDB_REF_NULL;
    244     ST_TOPO_UNLOCK;
    245 
    246     return rv;
    247 }
    248 
    249 
    250 /*
    251  * _nh_src_modid_enable
    252  *
    253  *   Enables the mod ID that will be used by the given destination
    254  *   CPU when sending NH packets.
    255  */
    256 STATIC int
    257 _nh_src_modid_enable(cpudb_ref_t db_ref, cpudb_entry_t *entry)
    258 {
    259     int                i;    
    260     int                nh_mod_id = -1;
    261     cpudb_entry_t      *l_entry;
    262     cpudb_unit_port_t  *sp;
    263     uint32             flags;    
    264 
    265     /*
    266      * If a reserved mod ID is used for NH,
    267      * nothing to do.  This reserved mod ID
    268      * should never be blocked.
    269      */
    270     if (bcm_st_reserved_modid_enable_get()) {
    271         return BCM_E_NONE;
    272     }
    273 
    274     l_entry = db_ref->local_entry;
    275 
    276     /* If not using the reserved modid feature,
    277      * enable remote mod ID since remote CPU will
    278      * be transmitting NH packets using its local mod ID.
    279      */
    280 
    281     /*
    282      * Get first mod ID in CPU entry
    283      */
    284     for (i = 0; i < entry->base.num_units; i++) {
    285         if (entry->mod_ids[i] >= 0) {
    286             nh_mod_id = entry->mod_ids[i];
    287             break;  /* Found a local mod ID */
    288         }
    289     }
    290 
    291     if (nh_mod_id < 0) {
    292         return BCM_E_NOT_FOUND;
    293     }
    294 
    295     for (i = 0; i < l_entry->base.num_stk_ports; i++) {
    296         sp = &l_entry->base.stk_ports[i];
    297         BCM_IF_ERROR_RETURN(bcm_stk_port_get(sp->unit, sp->port, &flags));
    298         if (!(flags & BCM_STK_SL) &&
    299             l_entry->sp_info[i].flags & CPUDB_SPF_RX_RESOLVED) {
    300             bcm_port_modid_enable_set(sp->unit, sp->port, nh_mod_id, TRUE);
    301         }
    302     }
    303 
    304     return BCM_E_NONE;
    305 }
    306 
    307 #if !defined(TOPO_PROCESSING_LOCAL)
    308 
    309 STATIC int
    310 topo_pkt_send(cpudb_ref_t db_ref, cpudb_entry_t *entry)
    311 {
    312     uint8 *buf, *start_buf;
    313     int len = 0;
    314     int rv = BCM_E_NONE;
    315 
    316     /* Now send out the packets */
    317     buf = start_buf = atp_tx_data_alloc(TOPO_PKT_BYTES_MAX);
    318     if (buf == NULL) {
    319         return BCM_E_MEMORY;
    320     }
    321 
    322     buf += CPUTRANS_HEADER_BYTES;
    323 
    324     topo_pkt_gen(db_ref, entry, buf, TOPO_PKT_BYTES_MAX, &len);
    325 
    326     /*
    327      * Enable mod ID that remote CPU will be using for NH packets
    328      */
    329     _nh_src_modid_enable(db_ref, entry);
    330 
    331     /*
    332      * Send topo packet requesting an immediate ACK back.
    333      *
    334      * This is necessary to avoid topo ACKs to travel
    335      * back with a NH mod-ID (as a result of the topo packet
    336      * processing) that the rest of the systems are not ready
    337      * to accept (such as the 32-mod ID support).
    338      */
    339     rv = atp_tx(entry->base.key,
    340                 BCM_ST_TOPO_CLIENT_ID,
    341                 start_buf,
    342                 CPUTRANS_HEADER_BYTES + len,
    343                 CPUTRANS_NO_HEADER_ALLOC | CPUTRANS_IMMEDIATE_ACK,
    344                 NULL,
    345                 NULL);
    346     if (rv < 0) {
    347         LOG_WARN(BSL_LS_TKS_TOPOLOGY,
    348                  (BSL_META("topo send %d:  %s. Failed to tx pkt to "
    349                            CPUDB_KEY_FMT_EOLN),
    350                   rv, bcm_errmsg(rv),
    351                   CPUDB_KEY_DISP(entry->base.key)));
    352     }
    353 
    354     atp_tx_data_free(start_buf);
    355     return rv;
    356 }
    357 
    358 /*
    359  * Send topo pkts in depth first
    360  * order - to avoid src mod blocking
    361  */
    362 STATIC int
    363 topo_pkt_send_depth_first(cpudb_ref_t db_ref, cpudb_entry_t *entry)
    364 {
    365     cpudb_stk_port_t *sp = NULL;
    366     cpudb_entry_t *other_end;
    367     int i, rv;
    368 
    369     if (CPUDB_TOPO_VISITED(entry)) {
    370         LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    371                   (BSL_META("TOPO ERROR: recursing infinitely")));
    372         return -1;
    373     }
    374 
    375     CPUDB_TOPO_VISITED_SET(entry);
    376 
    377     for (i = 0; i < entry->base.num_stk_ports; i++) {
    378         sp = &entry->sp_info[i];
    379 
    380         if (entry->sp_info[i].flags & CPUDB_SPF_TX_RESOLVED) {
    381             CPUDB_KEY_SEARCH(db_ref, sp->tx_cpu_key, other_end);
    382 
    383             if (other_end != NULL) {
    384                 if (CPUDB_TOPO_VISITED(other_end)) {
    385                     continue;
    386                 }
    387 
    388                 /* depth-first traversal */
    389                 topo_pkt_send_depth_first(db_ref, other_end);
    390            }
    391         }
    392     }
    393 
    394 #ifdef TOPO_PKT_SEND_CONFIG_IN_SET
    395     /* Only send a topo packet to an entry if CPUDB_F_CONFIG_IN is
    396        set.
    397 
    398        Normally, on the master, Discovery will have CONFIG_IN set for
    399        each slave entry that has participated in discovery.
    400 
    401        If #define TOPO_PKT_SEND_CONFIG_IN_SET is set and CPUDB entry flag
    402        CPUDB_F_CONFIG_IN is clear, then do not send a topo pkt to the
    403        entry.
    404 
    405        An application can clear CPUDB_F_CONFIG_IN before calling
    406        bcm_stack_topo_update() to suppress the transmission of a topo
    407        pkt to that entry. The entry will still be used for topology
    408        calculation and modid assignment.
    409        
    410        This could allow a system to change stacking software from a
    411        'stktask' based topo pkt protocol to something not using topo
    412        pkt by leaving those entries that need a topo pkt_send with
    413        CONFIG_IN set, and clearing CONFIG_IN from those that don't.
    414 
    415        The master is still responsible for ensuring that those systems
    416        with CONFIG_IN clear can still forward packets as needed for
    417        the systems with CONFIG_IN set.
    418 
    419     */
    420     if (entry != db_ref->master_entry && !entry->flags & CPUDB_F_CONFIG_IN) {
    421         return BCM_E_NONE;
    422     }
    423 #endif
    424 
    425     rv = topo_pkt_send(db_ref, entry);
    426     return rv;
    427 }
    428 
    429 #ifndef TOPO_DELAY_DEFAULT
    430 #define TOPO_DELAY_DEFAULT 500000 /* 0.5 second, for master only */
    431 #endif
    432 
    433 
    434 static int topo_delay_us = 0;
    435 static int topo_master_delay_us = TOPO_DELAY_DEFAULT;
    436 
    437 int
    438 topo_delay_set(int delay_us)
    439 {
    440     topo_delay_us = delay_us;
    441     return BCM_E_NONE;
    442 }
    443 
    444 int
    445 topo_delay_get(int *delay_us)
    446 {
    447     if (delay_us != NULL) {
    448         *delay_us = topo_delay_us;
    449     }
    450     return BCM_E_NONE;
    451 }
    452 
    453 int
    454 topo_master_delay_set(int delay_us)
    455 {
    456     topo_master_delay_us = delay_us;
    457     return BCM_E_NONE;
    458 }
    459 
    460 int
    461 topo_master_delay_get(int *delay_us)
    462 {
    463     if (delay_us != NULL) {
    464         *delay_us = topo_master_delay_us;
    465     }
    466     return BCM_E_NONE;
    467 }
    468 
    469 /*
    470  * Process current topo packet
    471  */
    472 
    473 STATIC void
    474 _topo_pkt_process_thread(void *cookie)
    475 {
    476     int rv;
    477     static topo_cpu_t topo_cpu;  /* Pretty big, so keep off the stack */
    478 
    479     COMPILER_REFERENCE(cookie);
    480 
    481     ST_TOPO_LOCK;
    482     /* Note that a previous invocation may have cleared a packet
    483        between the time the lock was released after calling
    484        topo_pkt_process(), and aquired above, so check if packet is
    485        still present. */
    486     if (TOPO_ACTIVE && TOPO_PKT_PRESENT) {
    487         /* Calculate local CPU connection information; call board programming */
    488         sal_memset(&topo_cpu, 0, sizeof(topo_cpu));
    489         rv = topo_pkt_parse(topo_current_db,
    490                             topo_current_db->local_entry,
    491                             topo_pkt_buf,
    492                             topo_pkt_len,
    493                             &topo_cpu,
    494                             NULL);
    495 
    496         if (rv < 0) {
    497             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    498                       (BSL_META("TOPO PKT ERROR: parse failure: %s\n"),
    499                        bcm_errmsg(rv)));
    500         } else if ((rv = topo_board_program(topo_current_db, &topo_cpu)) < 0) {
    501             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    502                       (BSL_META("TOPO PKT ERROR: appl fail: %s\n"),
    503                        bcm_errmsg(rv)));
    504         }
    505 
    506         TOPO_PKT_CLEAR; /* Discard packet once processed */
    507 
    508         if (rv == BCM_E_NONE) {
    509             int delay;
    510 
    511             /* Defer topology processing allowing all devices to update */
    512             delay = topo_delay_us;
    513             if (topo_current_db->local_entry ==
    514                     topo_current_db->master_entry) {
    515                 delay = topo_master_delay_us;
    516             }
    517             
    518             if ((topo_current_db->num_cpus > 1) && (delay > 0)) {
    519                 LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    520                             (BSL_META("TOPO: Sleeping for %d us\n"),
    521                              delay));
    522                 sal_usleep(delay);
    523             }
    524             /* Successfully processed topo pkt; signal stack task */
    525             bcm_st_event_send(BCM_STE_TOPO_SUCCESS);
    526         }
    527     }
    528     ST_TOPO_UNLOCK;
    529     sal_thread_exit(0);
    530 }
    531 
    532 STATIC void
    533 topo_pkt_process(void)
    534 {
    535     (void) sal_thread_create("topoDPC",
    536                              TOPO_THREAD_STACK,
    537                              TOPO_THREAD_PRIO,
    538                              _topo_pkt_process_thread,
    539                              NULL);
    540 }
    541   
    542 
    543 /*
    544  * This routine needs to handle the case where the local machine
    545  * hasn't realized it's ready for a topo pkt, but the master
    546  * has sent one.  This is done by queuing any topo packet received.
    547  */
    548 
    549 STATIC bcm_rx_t
    550 topo_pkt_handler(cpudb_key_t src_key,
    551                  int client_id,
    552                  bcm_pkt_t *pkt,
    553                  uint8 *payload,
    554                  int payload_len,
    555                  void *cookie)
    556 {
    557     COMPILER_REFERENCE(client_id);
    558     COMPILER_REFERENCE(cookie);
    559     COMPILER_REFERENCE(pkt);
    560 
    561     if (st_topo_lock == NULL) { /* Not active */
    562         return BCM_RX_HANDLED;
    563     }
    564 
    565     ST_TOPO_LOCK; /* Synchronize with topo_update call */
    566     if (!topo_enabled) {
    567         ST_TOPO_UNLOCK;
    568         return BCM_RX_HANDLED;
    569     }
    570     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    571                 (BSL_META("ST TOPO: pkt len %d in from "
    572                           CPUDB_KEY_FMT_EOLN),
    573                  payload_len, CPUDB_KEY_DISP(src_key)));
    574 
    575     /* Record this info - this may overwrite a previous packet that
    576        hasn't been processed yet. */
    577     TOPO_PKT_SET(payload, payload_len, src_key);
    578     if (TOPO_ACTIVE) { /* Topo is active meaning ready to process topo pkt */
    579         topo_pkt_process();
    580     }
    581 
    582     ST_TOPO_UNLOCK;
    583     return BCM_RX_HANDLED;
    584 }
    585 
    586 #endif  /* not topology generated locally */
    587 
    588 #if 1
    589 #define CHECK_PACK(_buf, _len, _cnt, _data) \
    590     if (_cnt + 1 > _len) return -1; else (_buf)[(_cnt)++] = _data
    591 #else /* Debug version of the function */
    592 #define CHECK_PACK(_buf, _len, _cnt, _data)  do { \
    593     LOG_INFO(BSL_LS_TKS_TOPOLOGY, \
    594              (BSL_META(\ \
    595                        (BSL_META"Packing mod %d\n")),
    596               _data)); \ \
    597     if (_cnt + 1 > _len) return -1; else (_buf)[(_cnt)++] = _data; \
    598 } while (0)
    599 #endif
    600 
    601 #if defined(INCLUDE_CHASSIS)
    602 
    603 /****************************************************************
    604  *
    605  * CHASSIS SPECIFIC CODE
    606  */
    607 
    608 
    609 
    610 /*
    611  * The following generate the chassis topology based on
    612  * conventions.  This is done by overriding the generation
    613  * of the TX and RX module-ID lists per stack port.
    614  */
    615 
    616 /* Pack mod IDs for an LM on an external LM stack port; sp_idx is active */
    617 STATIC int
    618 tp_chassis_lm_pack_mods(
    619     int cfm_count, /* How many CFMs active in the system? */
    620     /* The rest follow tp_pack_mods semantics */
    621     cpudb_ref_t db_ref, cpudb_entry_t *entry,
    622     uint8 *pkt_data, int len, int sp_idx, int tx)
    623 {
    624     int slot_id;  /* This slot ID */
    625     int cnt = 0; /* byte count */
    626     int cfm_slot;
    627     int slot, mod;
    628 
    629     slot_id = entry->base.slot_id;
    630     if ((cfm_slot = cpudb_sp_idx_to_slot(db_ref, entry, sp_idx, NULL)) < 0) {
    631         LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    632                     (BSL_META("TOPO: LM %d no cxn on %d\n"),
    633                      slot_id, sp_idx));
    634         return 0;
    635     }
    636 
    637     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    638                 (BSL_META("Packing LM slot %d, sp idx %d to slot %d, %s; %d cfms\n"),
    639                  slot_id, sp_idx, cfm_slot, tx ? "tx" : "rx", cfm_count));
    640 
    641     if (!tx) { /* RX: Expect any mod ID on any active stack port */
    642         CHECK_PACK(pkt_data, len, cnt, CFM_MODID(cfm_slot));
    643         FOREACH_OTHER_MODID(slot_id, slot, mod) {
    644             CHECK_PACK(pkt_data, len, cnt, mod);
    645         }
    646         return cnt;
    647     }
    648 
    649     /* TX: Pack mod IDs for transmitting to other LMs */
    650     CHECK_PACK(pkt_data, len, cnt, CFM_MODID(cfm_slot));
    651     if (cfm_count == 1) { /* Only one CFM in system */
    652         FOREACH_OTHER_MODID(slot_id, slot, mod) {
    653             CHECK_PACK(pkt_data, len, cnt, mod);
    654         }
    655     } else { /* assume cfm_count is 2; (if 0, sp is not active) */
    656         FOREACH_OTHER_MODID(slot_id, slot, mod) {
    657             if (cfm_slot % 2 == mod % 2) {
    658                 CHECK_PACK(pkt_data, len, cnt, mod);
    659             }
    660         }
    661     }
    662 
    663     return cnt;
    664 }
    665 
    666 /* Pack mod IDs for a CFM on an external LM stack port; sp_idx is active */
    667 STATIC int
    668 tp_chassis_cfm_pack_mods(
    669     int cfm_count, /* How many CFMs active in the system; > 0 */
    670     /* The rest follow tp_pack_mods semantics */
    671     cpudb_ref_t db_ref, cpudb_entry_t *entry,
    672     uint8 *pkt_data, int len, int sp_idx, int tx)
    673 {
    674     int slot_id;              /* local slot ID */
    675     int cnt = 0;              /* byte count */
    676     int lm_slot;              /* To which LM slot does sp_idx connect? */
    677     int mod;
    678 
    679     slot_id = entry->base.slot_id;
    680     if ((lm_slot = cpudb_sp_idx_to_slot(db_ref, entry, sp_idx, NULL)) < 0) {
    681         LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    682                     (BSL_META("TOPO: CFM %d no cxn on %d\n"),
    683                      slot_id, sp_idx));
    684         return 0;
    685     }
    686 
    687     LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
    688                 (BSL_META("Packing CFM slot %d sp idx %d to slot %d, %s; %d cfms\n"),
    689                  slot_id, sp_idx, lm_slot, tx ? "tx" : "rx", cfm_count));
    690 
    691     if (!tx) { /* If one CFM or "parity" matches, expect mod id */
    692         /* Expect other CFMs mod ID */
    693         CHECK_PACK(pkt_data, len, cnt, CFM_MODID(1-slot_id));
    694         FOREACH_MODID(lm_slot, mod) {
    695             if ((cfm_count == 1) || ((slot_id % 2) == (mod % 2))) {
    696                 CHECK_PACK(pkt_data, len, cnt, mod);
    697             }
    698         }
    699         return cnt;
    700     }
    701 
    702     CHECK_PACK(pkt_data, len, cnt, CFM_MODID(1-slot_id));
    703     FOREACH_MODID(lm_slot, mod) {    /* Send to all slot's mod ids */
    704         CHECK_PACK(pkt_data, len, cnt, mod);
    705     }
    706 
    707     return cnt;
    708 }
    709 
    710 STATIC int
    711 tp_chassis_pack_mods(
    712     cpudb_ref_t db_ref, cpudb_entry_t *entry,
    713     uint8 *pkt_data, int len, int sp_idx, int tx)
    714 {
    715     int cfm_count = 0;
    716     cpudb_entry_t *db_ent;
    717 
    718     /* If not resolved or enabled, no activity on the port */
    719     if ((entry->sp_info[sp_idx].flags & CPUDB_SPF_NO_LINK) ||
    720         (entry->sp_info[sp_idx].flags & CPUDB_SPF_INACTIVE) ||
    721         (entry->sp_info[sp_idx].flags & CPUDB_SPF_ETHERNET)) {
    722         return 0;
    723     }
    724 
    725     /* Count CFMs in DB */
    726     CPUDB_FOREACH_ENTRY(db_ref, db_ent) {
    727         if (db_ent->base.slot_id < 2) {
    728             ++cfm_count;
    729         }
    730     }
    731 
    732     if (entry->base.slot_id < 2) { /* This is a CFM card */
    733         return tp_chassis_cfm_pack_mods(cfm_count, db_ref, entry,
    734                                         pkt_data, len, sp_idx, tx);
    735     }
    736     
    737     return tp_chassis_lm_pack_mods(cfm_count, db_ref, entry, pkt_data, len,
    738                                    sp_idx, tx);
    739 }
    740 
    741 #else
    742 
    743 #define tp_chassis_pack_mods(a, b, c, d, e, f) 0
    744 
    745 #endif  /* INCLUDE_CHASSIS */
    746 
    747 
    748 /****************************************************************
    749  *
    750  * Topology packet generation and parsing.
    751  */
    752 
    753 STATIC int
    754 tp_pack_mods(cpudb_ref_t db_ref, cpudb_entry_t *entry,
    755              uint8 *pkt_data, int len, int sp_idx, int tx)
    756 {
    757     int i, nmod, mod;
    758     int entry_idx, cpu_idx;
    759     int cnt;
    760     cpudb_entry_t *cpu_entry;
    761 
    762 #if defined(INCLUDE_CHASSIS)
    763     if (CHASSIS_SM(db_ref->local_entry->base.flags)) {
    764         return tp_chassis_pack_mods(db_ref, entry, pkt_data,
    765                                     len, sp_idx, tx);
    766     }
    767 #endif
    768 
    769     entry_idx = entry->topo_idx;
    770     cnt = 0;
    771     CPUDB_FOREACH_ENTRY(db_ref, cpu_entry) {
    772         cpu_idx = cpu_entry->topo_idx;
    773         if (cpu_idx == entry_idx) {
    774             continue;
    775         }
    776 
    777         if (tx) {
    778             if (TP_TX_CXN(db_ref, entry_idx, cpu_idx) != sp_idx) {
    779                 continue;
    780             }
    781         } else {
    782             if (TP_RX_CXN(db_ref, entry_idx, cpu_idx) != sp_idx) {
    783                 continue;
    784             }
    785         }
    786 
    787         for (i = 0; i < cpu_entry->base.num_units; i++) {
    788 	    mod = cpu_entry->mod_ids[i];
    789 	    for (nmod = 0; nmod < cpu_entry->base.mod_ids_req[i]; nmod++) {
    790                 CHECK_PACK(pkt_data, len, cnt, (mod + nmod));
    791 	    }
    792         }
    793     }
    794 
    795     return cnt;
    796 }
    797 
    798 /*
    799  * Function:
    800  *      topo_pkt_gen
    801  * Purpose:
    802  *      Generate a topology packet for the given CPU entry
    803  * Parameters:
    804  *      db_ref              - DB to use
    805  *      entry               - Packet is destined for this entry's CPU
    806  *      pkt_data            - Packet buffer to use
    807  *      len                 - Bytes available in the buffer
    808  *      packed_bytes        - (OUT) bytes put in buffer
    809  * Returns:
    810  *      BCM_E_XXX
    811  * Notes:
    812  *      The base mod ID must be set for all entries before this is called.
    813  *      Assumes pkt_data points to the start of the topology payload
    814  *      (topo version) following the CPUTRANS header.
    815  */
    816 
    817 int
    818 topo_pkt_gen(cpudb_ref_t db_ref, cpudb_entry_t *entry,
    819              uint8 *pkt_data, int len, int *packed_bytes)
    820 {
    821     int sp_ofs;        /* Start of stack port entry */
    822     int rx_count_ofs;  /* Where count of rx mods goes */
    823     int cnt, sp_idx;
    824     cpudb_entry_t *db_ent;
    825     int i;
    826     uint8 *cur_ptr;
    827     int tot_bytes;
    828 
    829     if (TOPO_HEADER_BYTES(entry->base.num_units) > len) {
    830         return BCM_E_RESOURCE;
    831     }
    832 
    833     cur_ptr = &pkt_data[TOPO_VER_OFS];
    834     PACK_LONG(cur_ptr, TOPO_PKT_VERSION);
    835     cur_ptr += sizeof(uint32);
    836     PACK_LONG(cur_ptr, 0);  /* Reserved:  TOPO_RSVD_OFS */
    837     cur_ptr += sizeof(uint32);
    838     PACK_LONG(cur_ptr, db_ref->master_entry->base.dseq_num);
    839     cur_ptr += sizeof(uint32);
    840     cur_ptr += sizeof(uint32);  /* BASE_LEN_OFS: Base length is set later */
    841     PACK_LONG(cur_ptr, 0);   /* FLAGS  TOPO_FLAGS_OFS */
    842     cur_ptr += sizeof(uint32);
    843 
    844     /* Pack up module IDs per unit for this entry */
    845     for (i = 0; i < entry->base.num_units; i++) {
    846         PACK_LONG(cur_ptr, entry->mod_ids[i]);
    847         cur_ptr += sizeof(uint32);
    848     }
    849 
    850     tot_bytes = TOPO_HEADER_BYTES(entry->base.num_units);
    851     sp_ofs = TOPO_MOD_IDS_OFS + sizeof(uint32) * entry->base.num_units;
    852     /* Pack up stack info for this entry */
    853     for (sp_idx = 0; sp_idx < entry->base.num_stk_ports; sp_idx++) {
    854         pkt_data[sp_ofs] = 0; /* FLAGS */
    855         if (entry->sp_info[sp_idx].flags & CPUDB_SPF_CUT_PORT) {
    856             pkt_data[sp_ofs] |= TOPO_CUT_PORT;
    857         }
    858 
    859         cnt = tp_pack_mods(db_ref, entry, &pkt_data[sp_ofs + 2],
    860                            len - (sp_ofs + 2), sp_idx, TRUE);
    861         if (cnt < 0) {
    862             return BCM_E_RESOURCE;
    863         }
    864         pkt_data[sp_ofs + 1] = cnt;
    865         tot_bytes += cnt + 2;   /* FLAGS + num tx mods */
    866 
    867         rx_count_ofs = sp_ofs + cnt + 2;
    868         cnt = tp_pack_mods(db_ref, entry, &pkt_data[rx_count_ofs + 1],
    869                            len - (rx_count_ofs + 1), sp_idx, FALSE);
    870         if (cnt < 0) {
    871             return BCM_E_RESOURCE;
    872         }
    873         pkt_data[rx_count_ofs] = cnt;
    874         tot_bytes += cnt + 1;   /* num rx mods */
    875 
    876         sp_ofs = rx_count_ofs + 1 + cnt;
    877     }
    878 
    879     /* After the stack ports, add the dest mod ids, dest port per CPU */
    880     pkt_data[tot_bytes++] = db_ref->num_cpus;
    881     if (tot_bytes + (db_ref->num_cpus * (CPUDB_KEY_BYTES + 2)) > len) {
    882         return -1;
    883     }
    884     CPUDB_FOREACH_ENTRY(db_ref, db_ent) {
    885         CPUDB_KEY_PACK(&pkt_data[tot_bytes], db_ent->base.key);
    886         tot_bytes += CPUDB_KEY_BYTES;
    887         pkt_data[tot_bytes++] = db_ent->mod_ids[db_ent->base.dest_unit];
    888         /* Dest port may change with mod id remapping */
    889         pkt_data[tot_bytes++] = db_ent->base.dest_port;
    890     }
    891 
    892     if (tot_bytes + 4 > len) {
    893         return -1;
    894     }
    895 
    896     /* Clear app cookie; add to cur_ofs */
    897     pkt_data[tot_bytes++] = 0;
    898     pkt_data[tot_bytes++] = 0;
    899     pkt_data[tot_bytes++] = 0;
    900     pkt_data[tot_bytes++] = 0;
    901 
    902     /* Pack the length of the packet, from mod IDs thru ap cookie */
    903     PACK_LONG(&pkt_data[TOPO_BASE_LEN_OFS], tot_bytes - TOPO_FLAGS_OFS);
    904 
    905     if (packed_bytes != NULL) {
    906         *packed_bytes = tot_bytes;
    907     }
    908 
    909     return BCM_E_NONE;
    910 }
    911 
    912 
    913 /*
    914  * Function:
    915  *      topo_pkt_parse
    916  * Purpose:
    917  *      Parse a topology packet; checks data against DB ref
    918  * Parameters:
    919  *      db_ref              - DB to use
    920  *      entry               - (IN/OUT) The local entry (see notes)
    921  *      topo_data           - Packet buffer to use
    922  *      len                 - Length of buffer
    923  *      tp_cpu              - (OUT) Where to place results
    924  *      ap_data_ofs         - (OUT) offset of application data relative
    925  *                            to topo_data
    926  * Returns:
    927  *      BCM_E_FAIL if any mismatch is detected or buffer is too short.
    928  * Notes:
    929  *      Checks against local entry.  Length of buffer is only used
    930  *      for checking if there's not enough data in the packet.
    931  *
    932  *      Note that the local MOD IDs are recorded (taken from the packet)
    933  *      into entry->mod_ids (as well as into tp_cpu->mod_ids).
    934  */
    935 
    936 int
    937 topo_pkt_parse(
    938     cpudb_ref_t db_ref,
    939     cpudb_entry_t *entry,
    940     uint8 *topo_data,
    941     int len,
    942     topo_cpu_t *tp_cpu,
    943     int *ap_data_ofs)
    944 {
    945     int cur_ofs;                /* Index into topo_data */
    946     cpudb_base_t *l_base;       /* Local base info */
    947     int sp_idx;                 /* Index of stack port */
    948     topo_stk_port_t *tp_sp;     /* Topology stk port info */
    949     int i;
    950     cpudb_entry_t *db_ent;
    951     int cpu_count;
    952     cpudb_key_t db_key;
    953     int cpu_idx;
    954     uint8 *cur_ptr;
    955     int num_modids;
    956 
    957     if     (entry == NULL ||
    958             db_ref == CPUDB_REF_NULL ||
    959             topo_data == NULL ||
    960             tp_cpu == NULL) {
    961         return BCM_E_PARAM;
    962     }
    963 
    964     l_base = &entry->base;
    965 
    966     if (len < TOPO_HEADER_BYTES(db_ref->num_cpus) + 5) {
    967         LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
    968                   (BSL_META("TOPO PARSE ERROR: Packet too short (%d<%d)[#%d]\n"),
    969                    len,
    970                    (int)TOPO_HEADER_BYTES(db_ref->num_cpus) + 5,
    971                    db_ref->num_cpus));
    972         return BCM_E_FAIL;
    973     }
    974     cur_ptr = &topo_data[TOPO_VER_OFS];
    975     UNPACK_LONG(cur_ptr, tp_cpu->version);        /* TOPO_VER_OFS */
    976     cur_ptr += sizeof(uint32);
    977     cur_ptr += sizeof(uint32);   /* Reserved:  TOPO_RSVD_OFS */
    978     UNPACK_LONG(cur_ptr, tp_cpu->master_seq_num); /* TOPO_MSEQ_NUM_OFS */
    979     cur_ptr += sizeof(uint32);
    980     /* skip TOPO_BASE_LEN_OFS */
    981     cur_ptr += sizeof(uint32);
    982     UNPACK_LONG(cur_ptr, tp_cpu->flags);          /* TOPO_FLAGS_OFS */
    983     cur_ptr += sizeof(uint32);
    984 
    985     /* Pull the local device module IDs off of the packet */
    986     for (i = 0; i < entry->base.num_units; i++) {
    987         UNPACK_LONG(cur_ptr, entry->mod_ids[i]);
    988         cur_ptr += sizeof(uint32);
    989     }
    990 
    991     /* Now parse the mod id lists per local stack port */
    992     cur_ofs = TOPO_MOD_IDS_OFS + sizeof(uint32) * entry->base.num_units;
    993     for (sp_idx = 0; sp_idx < l_base->num_stk_ports; sp_idx++) {
    994         tp_sp = &tp_cpu->tp_stk_port[sp_idx];
    995 
    996         tp_sp->flags = topo_data[cur_ofs++];
    997 
    998         /* Check cut port flag */
    999         if (tp_sp->flags & TOPO_CUT_PORT) {
   1000             entry->sp_info[sp_idx].flags |= CPUDB_SPF_CUT_PORT;
   1001         } else {
   1002             entry->sp_info[sp_idx].flags &= ~CPUDB_SPF_CUT_PORT;
   1003         }
   1004 
   1005         /* Get TX modid count and check database limit */
   1006         num_modids = topo_data[cur_ofs++];
   1007         if (num_modids > TOPO_MODIDS_MAX) {
   1008             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1009                       (BSL_META("TOPO PARSE ERROR: TX mod id count %d exceeds DB limit "
   1010                        "%d\n"), num_modids, TOPO_MODIDS_MAX));
   1011             return BCM_E_FAIL;
   1012         }
   1013         tp_sp->tx_mod_num = num_modids;
   1014         for (i = 0; i < tp_sp->tx_mod_num; i++) {
   1015             tp_sp->tx_mods[i] = topo_data[cur_ofs++];
   1016             LOG_INFO(BSL_LS_TKS_TOPOLOGY,
   1017                      (BSL_META("ST TOPO: Map TX mod %d to stk idx %d\n"),
   1018                       tp_sp->tx_mods[i], sp_idx));
   1019         }
   1020 
   1021         /* Get RX modid count and check database limit */
   1022         num_modids = topo_data[cur_ofs++];
   1023         if (num_modids > TOPO_MODIDS_MAX) {
   1024             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1025                       (BSL_META("TOPO PARSE ERROR: RX mod id count %d exceeds DB limit "
   1026                        "%d\n"), num_modids, TOPO_MODIDS_MAX));
   1027             return BCM_E_FAIL;
   1028         }
   1029         tp_sp->rx_mod_num = num_modids;
   1030         for (i = 0; i < tp_sp->rx_mod_num; i++) {
   1031             tp_sp->rx_mods[i] = topo_data[cur_ofs++];
   1032             LOG_INFO(BSL_LS_TKS_TOPOLOGY,
   1033                      (BSL_META("ST TOPO: Map RX mod %d to stk idx %d\n"),
   1034                       tp_sp->rx_mods[i], sp_idx));
   1035         }
   1036         
   1037         if (cur_ofs + 2 > len) {
   1038             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1039                       (BSL_META("TOPO PARSE ERROR: Packet too short at %d\n"),
   1040                        cur_ofs));
   1041             return BCM_E_FAIL;
   1042         }
   1043     }
   1044 
   1045     /* Now that mod ids are unpacked, copy local entry info to tp_cpu */
   1046     cpudb_entry_copy(&tp_cpu->local_entry, entry);
   1047 
   1048     /* The base mod ids per CPU follow stack port info */
   1049     cpu_count = topo_data[cur_ofs++];
   1050     if (cur_ofs + cpu_count * (CPUDB_KEY_BYTES + 1) > len) {
   1051         LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1052                   (BSL_META("TOPO PARSE ERROR: Packet too short at %d\n"),
   1053                    cur_ofs));
   1054         return BCM_E_FAIL;
   1055     }
   1056     for (cpu_idx = 0; cpu_idx < cpu_count; cpu_idx++) {
   1057         CPUDB_KEY_UNPACK(&topo_data[cur_ofs], db_key);
   1058         cur_ofs += CPUDB_KEY_BYTES;
   1059         CPUDB_KEY_SEARCH(db_ref, db_key, db_ent);
   1060         if (db_ent == NULL) {
   1061             LOG_ERROR(BSL_LS_TKS_TOPOLOGY,
   1062                       (BSL_META("TOPO PARSE ERROR: Could not find DB key "
   1063                                 CPUDB_KEY_FMT_EOLN),
   1064                        CPUDB_KEY_DISP(db_key)));
   1065             return BCM_E_FAIL;
   1066         }
   1067         db_ent->dest_mod = topo_data[cur_ofs++];
   1068         db_ent->dest_port = topo_data[cur_ofs++];
   1069         db_ent->flags |= CPUDB_F_DEST_KNOWN;
   1070         LOG_VERBOSE(BSL_LS_TKS_TOPOLOGY,
   1071                     (BSL_META("TOPO: Mapped " CPUDB_KEY_FMT " to (%d, %d)\n"),
   1072                      CPUDB_KEY_DISP(db_key), db_ent->dest_mod,
   1073                      db_ent->dest_port));
   1074     }
   1075 
   1076     UNPACK_LONG(&topo_data[cur_ofs], tp_cpu->ap_cookie);
   1077     cur_ofs += sizeof(uint32);
   1078     if (ap_data_ofs != NULL) {
   1079         *ap_data_ofs = cur_ofs;
   1080     }
   1081 
   1082     return BCM_E_NONE;
   1083 }