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

nh_tx.c (29912B)


      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:        nh_tx.c
      8  * Purpose:     Next hop transport services
      9  * Requires:
     10  * Notes:
     11  *    A per-stack port transport pointer is supported.  This is
     12  *    primarily for supporting communication on Linux between the
     13  *    kernel and user modes.
     14  */
     15 
     16 #include <shared/bsl.h>
     17 
     18 #include <shared/idents.h>
     19 
     20 #include <sal/core/sync.h>
     21 #include <sal/core/libc.h>
     22 #include <sal/core/thread.h>
     23 #include <shared/alloc.h>
     24 
     25 #include <bcm/types.h>
     26 #include <bcm/pkt.h>
     27 #include <bcm/tx.h>
     28 #include <bcm/error.h>
     29 #include <bcm/l2.h>
     30 
     31 #include <appl/cputrans/nh_tx.h>
     32 #include <appl/cputrans/cputrans.h>
     33 
     34 #include "t_util.h"
     35 
     36 #define STK_PORTS_MAX	CPUDB_CXN_MAX
     37 
     38 /* Packet parameters */
     39 static bcm_mac_t nh_dest_mac           = NH_TX_DEST_MAC_DEFAULT;
     40 static bcm_mac_t nh_snap_mac           = NH_TX_SNAP_MAC_DEFAULT;
     41 
     42 /* These make resetting easier */
     43 static bcm_mac_t nh_dest_mac_default   = NH_TX_DEST_MAC_DEFAULT;
     44 static bcm_mac_t nh_snap_mac_default   = NH_TX_SNAP_MAC_DEFAULT;
     45 
     46 /* The following are in host order */
     47 static uint16 nh_snap_type             = NH_TX_SNAP_TYPE_DEFAULT;
     48 static uint16 nh_local_type            = NH_TX_LOCAL_PKT_TYPE;
     49 
     50 static int nh_dest_port                = NH_TX_DEST_PORT_DEFAULT;
     51 static int nh_dest_mod                 = NH_TX_DEST_MOD_DEFAULT;
     52 
     53 static int nh_src_port                 = NH_TX_SRC_PORT_DEFAULT;
     54 static int nh_src_mod                  = NH_TX_SRC_MOD_DEFAULT;
     55 static int nh_tx_unknown_modid         = NH_TX_SRC_MOD_DEFAULT;
     56 
     57 static bcm_mac_t nh_local_mac;
     58 
     59 static sal_mutex_t nh_mutex;
     60 
     61 static int init_done;
     62 static int setup_done;
     63 
     64 static volatile int pending_count;
     65 
     66 static bcm_trans_ptr_t nh_trans_ptr;
     67 
     68 /*
     69  * Local stack ports.  (unit, port) and flag to indicate duplex
     70  */
     71 static struct stk_port_t {
     72     int unit;
     73     int port;
     74     bcm_trans_ptr_t *trans_ptr;
     75     int src_mod;   /* For stack-port specific destination info */
     76     int src_port;
     77 } nh_stk_ports[STK_PORTS_MAX];
     78 static int num_stk_ports;
     79 
     80 #define NH_LOCK sal_mutex_take(nh_mutex, sal_sem_FOREVER);
     81 #define NH_UNLOCK sal_mutex_give(nh_mutex);
     82 #define NH_INIT if (!init_done) BCM_IF_ERROR_RETURN(nh_init())
     83 
     84 /* Forward declarations */
     85 STATIC int nh_init(void);
     86 STATIC void nh_tx_callback(int unit, bcm_pkt_t *pkt, void *cookie);
     87 STATIC void nh_tx_callback_nofree(int unit, bcm_pkt_t *pkt, void *cookie);
     88 STATIC void _nh_tx_l2_header_setup(uint8 *pkt_buf, uint16 vlan);
     89 
     90 typedef struct {
     91     nh_tx_cb_f callback;
     92 } nh_cb_cookie_t;
     93 
     94 STATIC void *
     95 _nh_callback_cookie_set(nh_tx_cb_f callback)
     96 {
     97     nh_cb_cookie_t *cookie = sal_alloc(sizeof(*cookie), "nh_cookie");
     98 
     99     if (cookie) {
    100         cookie->callback = callback;
    101     }
    102 
    103     return (void *)cookie;
    104 }
    105 
    106 STATIC void
    107 _nh_callback_cookie_get(void *cookie, nh_tx_cb_f *cb)
    108 {
    109     if (cookie) {
    110         nh_cb_cookie_t *nh_cookie = (nh_cb_cookie_t *)cookie;
    111         *cb = nh_cookie->callback;
    112         sal_free(nh_cookie);
    113     } else {
    114         *cb = NULL;
    115     }
    116 }
    117 
    118 /*
    119  * Function:
    120  *      nh_tx_setup
    121  * Purpose:
    122  *      Set up the next hop subsystem
    123  * Parameters:
    124  *      trans_ptr    - Pointer to transport function structure
    125  * Returns:
    126  *      BCM_E_XXX
    127  * Notes:
    128  */
    129 
    130 int
    131 nh_tx_setup(bcm_trans_ptr_t *trans_ptr)
    132 {
    133     NH_INIT;
    134 
    135     if (trans_ptr->tp_tx == NULL) {
    136         return BCM_E_PARAM;
    137     }
    138 
    139     sal_memcpy(&nh_trans_ptr, trans_ptr, sizeof(bcm_trans_ptr_t));
    140 
    141     setup_done = TRUE;
    142 
    143     return BCM_E_NONE;
    144 }
    145 
    146 
    147 /*
    148  * Function:
    149  *      nh_tx_setup_done
    150  * Purpose:
    151  *      Indicate if nh_tx has been setup
    152  * Parameters:
    153  *      None
    154  * Returns:
    155  *      Boolean:  FALSE if not setup, TRUE if setup
    156  * Notes:
    157  */
    158 
    159 int
    160 nh_tx_setup_done(void)
    161 {
    162     return setup_done;
    163 }
    164 
    165 /*
    166  * Function:
    167  *      nh_tx_trans_ptr_set
    168  * Purpose:
    169  *      Set the transport pointer for the given stack port
    170  * Parameters:
    171  *      unit               - The unit on which the stack port lies
    172  *      port               - The physical port of the stack port
    173  *      ptr                - The pointer to set to
    174  * Returns:
    175  *      BCM_E_XXX
    176  * Notes:
    177  *      Setting to NULL will remove the entry; good for cleaning
    178  *      up if stack ports are coming and going.
    179  */
    180 
    181 int
    182 nh_tx_trans_ptr_set(int unit, int port, bcm_trans_ptr_t *ptr)
    183 {
    184     int i, j;
    185 
    186     NH_INIT;
    187     NH_LOCK;
    188     for (i = 0; i < num_stk_ports; i++) {
    189         if (nh_stk_ports[i].unit == unit &&
    190 	    nh_stk_ports[i].port == port) {
    191             if (ptr == NULL) {    /* If ptr == NULL, remove the entry */
    192                 for (j = i; j < num_stk_ports - 1; j++) {
    193                     nh_stk_ports[j] = nh_stk_ports[j + 1];
    194                 }
    195                 nh_stk_ports[num_stk_ports - 1].trans_ptr = NULL;
    196                 nh_stk_ports[num_stk_ports - 1].unit = -1;
    197                 nh_stk_ports[num_stk_ports - 1].port = -1;
    198                 num_stk_ports--;
    199             } else {
    200                 nh_stk_ports[i].trans_ptr = ptr;
    201             }
    202             NH_UNLOCK;
    203             return BCM_E_NONE;
    204         }
    205     }
    206 
    207     if (ptr != NULL) {       /* Add new entry */
    208         if (num_stk_ports >= STK_PORTS_MAX) {
    209             NH_UNLOCK;
    210             return BCM_E_RESOURCE;
    211         }
    212         nh_stk_ports[num_stk_ports].unit = unit;
    213         nh_stk_ports[num_stk_ports].port = port;
    214         nh_stk_ports[num_stk_ports].trans_ptr = ptr;
    215         num_stk_ports++;
    216     }
    217 
    218     NH_UNLOCK;
    219     return BCM_E_NONE;
    220 }
    221 
    222 
    223 /*
    224  * Function:
    225  *      nh_tx_trans_ptr_get
    226  * Purpose:
    227  *      Get the transport pointer for the given stack port
    228  * Parameters:
    229  *      unit               - The unit on which the stack port lies
    230  *      port               - The physical port of the stack port
    231  *      ptr                - (OUT) Set to port's pointer
    232  * Returns:
    233  *      BCM_E_XXX
    234  */
    235 
    236 int
    237 nh_tx_trans_ptr_get(int unit, int port, bcm_trans_ptr_t **ptr)
    238 {
    239     int i;
    240     int rv = BCM_E_NOT_FOUND;
    241 
    242     NH_INIT;
    243     NH_LOCK;
    244     for (i = 0; i < num_stk_ports; i++) {
    245         if (nh_stk_ports[i].unit == unit &&
    246 	    nh_stk_ports[i].port == port) {
    247             *ptr = nh_stk_ports[i].trans_ptr;
    248             rv = BCM_E_NONE;
    249             break;
    250         }
    251     }
    252     NH_UNLOCK;
    253 
    254     return rv;
    255 }
    256 
    257 /*
    258  * In general, these should only be called by next_hop to keep
    259  * the information consistent.
    260  *
    261  * Setting src_mod/port to < 0 will result in using the default
    262  * setting (from nh_mod_id, etc).
    263  */
    264 
    265 int
    266 nh_tx_src_mod_port_set(int unit, int port, int src_mod, int src_port)
    267 {
    268     int i;
    269     int rv = BCM_E_NOT_FOUND;
    270 
    271     NH_INIT;
    272     NH_LOCK;
    273     for (i = 0; i < num_stk_ports; i++) {
    274         if (nh_stk_ports[i].unit == unit &&
    275             nh_stk_ports[i].port == port) {
    276             
    277             LOG_INFO(BSL_LS_TKS_NH,
    278                      (BSL_META_U(unit,
    279                                  "NH: Set unit %d, port %d, src-mod %d, src-port %d\n"),
    280                       unit, port, src_mod, src_port));
    281 
    282             nh_stk_ports[i].src_mod = src_mod;
    283             nh_stk_ports[i].src_port = src_port;
    284             rv = BCM_E_NONE;
    285             break;
    286         }
    287     }
    288 
    289     if (rv == BCM_E_NOT_FOUND) {
    290         if (num_stk_ports < STK_PORTS_MAX) {
    291             
    292             LOG_INFO(BSL_LS_TKS_NH,
    293                      (BSL_META_U(unit,
    294                                  "NH: Add unit %d, port %d, src-mod %d, src-port %d\n"),
    295                       unit, port, src_mod, src_port));
    296             
    297             nh_stk_ports[num_stk_ports].unit = unit;
    298             nh_stk_ports[num_stk_ports].port = port;
    299             nh_stk_ports[num_stk_ports].src_mod = src_mod;
    300             nh_stk_ports[num_stk_ports].src_port = src_port;
    301             num_stk_ports++;
    302             rv = BCM_E_NONE;
    303         }
    304     }
    305 
    306     NH_UNLOCK;
    307 
    308     return rv;
    309 }
    310 
    311 int
    312 nh_tx_src_mod_port_get(int unit, int port, int *src_mod, int *src_port)
    313 {
    314     int i;
    315     int rv = BCM_E_NOT_FOUND;
    316 
    317     NH_INIT;
    318     NH_LOCK;
    319     for (i = 0; i < num_stk_ports; i++) {
    320         if (nh_stk_ports[i].unit == unit &&
    321             nh_stk_ports[i].port == port) {
    322 
    323             if (src_mod != NULL) {
    324                 *src_mod = nh_stk_ports[i].src_mod;
    325             }
    326             if (src_port != NULL) {
    327                 *src_port = nh_stk_ports[i].src_port;
    328             }
    329             rv = BCM_E_NONE;
    330             break;
    331         }
    332     }
    333     NH_UNLOCK
    334 
    335     return rv;
    336 }
    337 
    338 /*
    339  * Set the src mod-id, src port for a list of packets according
    340  * to which stack port they're going out
    341  */
    342 STATIC void
    343 pkt_list_src_mod_id_port_set(bcm_pkt_t *pkt)
    344 {
    345     int smod = nh_src_mod;      /* Default values */
    346     int sport = nh_src_port;
    347     int port;
    348     int i;
    349     bcm_pkt_t *cur_pkt;
    350 
    351     /* Find the port on which we're transmitting */
    352     BCM_PBMP_ITER(pkt->tx_pbmp, port) {
    353         break;
    354     }
    355 
    356     NH_LOCK;
    357     for (i = 0; i < num_stk_ports; i++) {
    358         if (nh_stk_ports[i].unit == pkt->unit &&
    359 	    nh_stk_ports[i].port == port) {
    360             if (nh_stk_ports[i].src_mod >= 0) {
    361                 smod = nh_stk_ports[i].src_mod;
    362             }
    363             if (nh_stk_ports[i].src_port >= 0) {
    364                 sport = nh_stk_ports[i].src_port;
    365             }
    366         }
    367     }
    368     NH_UNLOCK;
    369 
    370     for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) {
    371         
    372         cur_pkt->flags |= (BCM_TX_SRC_MOD|BCM_TX_SRC_PORT);
    373         cur_pkt->src_mod = smod;
    374         cur_pkt->src_port = sport;
    375     }
    376 }
    377 
    378 /*
    379  * If the unit/port has a transport pointer assigned, use that.
    380  * otherwise, use the high level one
    381  */
    382 STATIC bcm_trans_ptr_t *
    383 resolve_trans_ptr(int unit, int port)
    384 {
    385     int i;
    386     bcm_trans_ptr_t *rv =  &nh_trans_ptr;
    387 
    388     NH_LOCK;
    389     for (i = 0; i < num_stk_ports; i++) {
    390         if (nh_stk_ports[i].unit == unit &&
    391 	    nh_stk_ports[i].port == port) {
    392 	    if (nh_stk_ports[i].trans_ptr != NULL) {
    393                 rv = nh_stk_ports[i].trans_ptr;
    394             }
    395             break;
    396         }
    397     }
    398     NH_UNLOCK;
    399 
    400     return rv;
    401 }
    402 
    403 /*
    404  * As above, but for packet
    405  */
    406 STATIC bcm_trans_ptr_t *
    407 resolve_pkt_trans_ptr(bcm_pkt_t *pkt)
    408 {
    409     bcm_port_t port;
    410 
    411     BCM_PBMP_ITER(pkt->tx_pbmp, port) {
    412         break;
    413     }
    414 
    415     return resolve_trans_ptr(pkt->unit, port);
    416 }
    417 
    418 /*
    419  * Function:
    420  *      nh_tx_dest_(set|get)
    421  * Purpose:
    422  *      Set/get the parameters used for default next hop destination settings
    423  *      Note that these values may be overridden on a per-stack-port
    424  *      basis.
    425  * Parameters:
    426  *      mac              - (OUT for get) The destination mac address used
    427  *      dest_mod         - (OUT for get) The destination mod id used
    428  *      dest_port        - (OUT for get) The destination port used
    429  * Returns:
    430  *      BCM_E_XXX
    431  * Notes:
    432  *      No checking is done for "legality" of values.
    433  */
    434 
    435 int
    436 nh_tx_dest_set(const bcm_mac_t mac, int dest_mod, int dest_port)
    437 {
    438     nh_dest_mod = dest_mod;
    439     nh_dest_port = dest_port;
    440     sal_memcpy(nh_dest_mac, mac, sizeof(bcm_mac_t));
    441 
    442     return BCM_E_NONE;
    443 }
    444 
    445 int
    446 nh_tx_dest_get(bcm_mac_t *mac, int *dest_mod, int *dest_port)
    447 {
    448     if (dest_mod != NULL) {
    449         *dest_mod = nh_dest_mod;
    450     }
    451     if (dest_port != NULL) {
    452         *dest_port = nh_dest_port;
    453     }
    454     if (mac != NULL) {
    455         sal_memcpy(mac, nh_dest_mac, sizeof(bcm_mac_t));
    456     }
    457 
    458     return BCM_E_NONE;
    459 }
    460 
    461 /*
    462  * Install or remove the NH TX dest mac into all local L2 tables
    463  */
    464 
    465 int
    466 nh_tx_dest_install(int install, int vid)
    467 {
    468     bcm_l2_addr_t nh_l2_addr;
    469     int unit;
    470     int rv;
    471 
    472     bcm_l2_addr_t_init(&nh_l2_addr, nh_dest_mac, vid);
    473     nh_l2_addr.flags |= BCM_L2_STATIC;
    474     nh_l2_addr.flags |= BCM_L2_LOCAL_CPU;
    475     BCM_FOREACH_LOCAL_UNIT(unit) {
    476         if (install) {
    477             rv = bcm_l2_addr_add(unit, &nh_l2_addr);
    478             if ((rv != BCM_E_UNAVAIL) && (rv != BCM_E_NONE)) {
    479                 LOG_ERROR(BSL_LS_TKS_NH,
    480                           (BSL_META("NH TX ERROR adding L2 addr to "
    481                                     "unit %d: %s\n"),
    482                            unit, bcm_errmsg(rv)));
    483                 return rv;
    484             }
    485         } else {
    486             (void)bcm_l2_addr_delete(unit, nh_dest_mac, vid);
    487         }
    488     }
    489 
    490     return BCM_E_NONE;
    491 }
    492 
    493 /*
    494  * Function:
    495  *      nh_tx_src_(set|get)
    496  * Purpose:
    497  *      Set/get the parameters used for next hop source settings
    498  * Parameters:
    499  *      src_mod   - (OUT for get) The source mod id used
    500  *      src_port  - (OUT for get) The source port used
    501  * Returns:
    502  *      BCM_E_XXX
    503  * Notes:
    504  *      No checking is done for "legality" of values.
    505  */
    506 
    507 int
    508 nh_tx_src_set(int src_mod, int src_port)
    509 {
    510     nh_src_mod = src_mod;
    511     nh_src_port = src_port;
    512 
    513     return BCM_E_NONE;
    514 }
    515 
    516 int
    517 nh_tx_src_get(int *src_mod, int *src_port)
    518 {
    519     *src_mod = nh_src_mod;
    520     *src_port = nh_src_port;
    521 
    522     return BCM_E_NONE;
    523 }
    524 
    525 
    526 /* Resolve dest mod/port IDs for packet, based on transmit unit/port */
    527 
    528 STATIC void
    529 _nh_src_mod_id_port_get(int unit, int port, int *src_mod, int *src_port)
    530 {
    531     int i;
    532 
    533     *src_mod = nh_src_mod;            /* Default values */
    534     *src_port = nh_src_port;
    535 
    536     /* Check to see if overridden by port specific values */
    537     NH_LOCK;
    538     for (i = 0; i < num_stk_ports; i++) {
    539         if (nh_stk_ports[i].unit == unit &&
    540             nh_stk_ports[i].port == port) {
    541             if (nh_stk_ports[i].src_mod >= 0) {
    542                 *src_mod = nh_stk_ports[i].src_mod;
    543             }
    544             if (nh_stk_ports[i].src_port >= 0) {
    545                 *src_port = nh_stk_ports[i].src_port;
    546             }
    547             break;
    548         }
    549     }
    550     NH_UNLOCK;
    551 }
    552 
    553 /*
    554  * Set up a next hop packet before we know what (unit, port) it will
    555  * go out on; this sets up the dest port/mod, the COS, the internal priority,
    556  * and gives it the default source mod/port.  If the CPUTRANS_NO_L2_UPDATE
    557  * is not set, then the L2 header is also updated.
    558  *
    559  * The 'cos' parameter contains the cos and internal priority
    560  * values encoded.   Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET()
    561  * to set values accordingly.  The internal priority is optional;
    562  * if this is not provided, the cos value is used for internal priority.
    563  */
    564 
    565 STATIC void
    566 _nh_pkt_setup(bcm_pkt_t *pkt, int cos, uint16 vlan,
    567               uint16 type, uint32 flags)
    568 {
    569     uint8 *pkt_data;
    570 
    571     pkt->opcode = BCM_HG_OPCODE_CPU;
    572     pkt->dest_port = nh_dest_port;
    573     pkt->dest_mod = nh_dest_mod;
    574     pkt->src_mod = nh_src_mod; /* Set up with defaults */
    575     pkt->src_port = nh_src_port;
    576     pkt->flags |= BCM_TX_SRC_MOD | BCM_TX_SRC_PORT;
    577 
    578     /* If internal priority is not provided, set value to cos */
    579     pkt->cos = CPUTRANS_COS_GET(cos);
    580     if (cos & CPUTRANS_INT_PRIO_VALID) {
    581         pkt->prio_int = CPUTRANS_INT_PRIO_GET(cos);
    582     } else {
    583         pkt->prio_int = pkt->cos;
    584     }
    585     pkt->flags |= BCM_TX_PRIO_INT;
    586 
    587     /* Pack the transport multiplexing type and the unit/port into pkt */
    588     pkt_data = &(pkt->pkt_data[0].data[CPUTRANS_TR_MULT_OFS]);
    589     PACK_SHORT(pkt_data, type);
    590     if (!(flags & CPUTRANS_NO_L2_UPDATE)) {
    591         _nh_tx_l2_header_setup(pkt->pkt_data[0].data,
    592                                BCM_VLAN_CTRL(cos, 0, vlan));
    593     }
    594 }
    595 
    596 /*
    597  * Final setup of a next hop packet once the local TX unit, port are known
    598  * This sets the source mod/port on a per-stack port basis (if configured).
    599  * The transmit unit and port are set in the packet data.  If a final
    600  * setup is programmed into the transport pointer, that is called as
    601  * well.
    602  */
    603 
    604 STATIC void
    605 _nh_pkt_local_setup(bcm_pkt_t *pkt, int unit, int port, int nh_hdr_update)
    606 {
    607     uint8 *pkt_data;
    608     bcm_trans_ptr_t *tp;
    609     int smod, sport;
    610 
    611     _nh_src_mod_id_port_get(unit, port, &smod, &sport);
    612     pkt->flags |= (BCM_TX_SRC_MOD|BCM_TX_SRC_PORT);
    613     pkt->src_mod = smod;
    614     pkt->src_port = sport;
    615     
    616     pkt->unit = unit;
    617     BCM_PBMP_PORT_SET(pkt->tx_pbmp, port);
    618 
    619     if (nh_hdr_update) {
    620         /* Set the unit/port in the header */
    621         pkt_data = &(pkt->pkt_data[0].data[CPUTRANS_TR_RSVD_OFS]);
    622         *pkt_data++ = (uint8)unit;
    623         *pkt_data = (uint8)port;
    624     }
    625 
    626     tp = resolve_trans_ptr(unit, port);
    627     if (tp->tp_setup_tx != NULL) {
    628         /* Final setup of packet for unit type */
    629         tp->tp_setup_tx(unit, pkt);
    630     }
    631 }
    632 
    633 /*
    634  * Function:
    635  *      nh_tx
    636  * Purpose:
    637  *      Transmit a next hop packet.
    638  * Parameters:
    639  *      unit       - Which unit to send on
    640  *      port       - Which port to send out
    641  *      pkt_buf    - Packet buffer
    642  *      len        - Total length of packet
    643  *      cos        - Cos and internal priority
    644  *      vlan       - VLAN
    645  *      pkt_type   - Type marker to use in header
    646  *      ct_flags   - See below.
    647  *      callback   - If non-null, send async and make callback
    648  *      cookie     - Cookie for callback.
    649  * Returns:
    650  *      BCM_E_XXX
    651  * Notes:
    652  *      Does not do segmentation.
    653  *
    654  *      Flags:
    655  *          CPUTRANS_NO_HEADER_ALLOC   - The packet already contains
    656  *              space for the CPUTRANS header.
    657  *          CPUTRANS_NO_L2_UPDATE      - Do not update the L2 header
    658  *              of the packet before forwarding.
    659  *
    660  *      The 'cos' parameter contains the cos and internal priority
    661  *      values encoded.   Use CPUTRANS_COS_SET() CPUTRANS_INT_PRIO_SET()
    662  *      to set values accordingly.  The internal priority is optional;
    663  *      if this is not provided, the cos value is used for internal priority.
    664  */
    665 
    666 int
    667 nh_tx(int unit,
    668       int port,
    669       uint8 *pkt_buf,
    670       int len,
    671       int cos,
    672       int vlan,
    673       uint16 pkt_type,
    674       uint32 ct_flags,
    675       nh_tx_cb_f callback,
    676       void *cookie)
    677 {
    678     bcm_pkt_t *pkt;
    679     int rv;
    680     bcm_trans_ptr_t *tp;
    681     nh_cb_cookie_t *nh_cookie = NULL;
    682 
    683     if (!setup_done) {
    684         return BCM_E_INIT;
    685     }
    686 
    687     LOG_DEBUG(BSL_LS_TKS_NH,
    688               (BSL_META_U(unit,
    689                           "NHTX: (%d, %d). %p, len %d, type %d. flags %x "
    690                           "cb %p, cookie %p\n"),
    691                unit, port, pkt_buf, len, pkt_type, ct_flags,
    692                callback, cookie));
    693 
    694     /* Force single packet allocation */
    695     pkt = cputrans_tx_pkt_alloc(pkt_buf, len, ct_flags);
    696     if (pkt == NULL) {
    697         return BCM_E_RESOURCE;
    698     }
    699 
    700     _nh_pkt_setup(pkt, cos, vlan, pkt_type, ct_flags);
    701     _nh_pkt_local_setup(pkt, unit, port, TRUE);
    702 
    703     if (callback != NULL) {
    704         pkt->cookie = cookie;
    705         pkt->call_back = nh_tx_callback;
    706         nh_cookie = _nh_callback_cookie_set(callback);
    707     } else {
    708         pkt->call_back = NULL;
    709     }
    710 
    711     tp = resolve_trans_ptr(unit, port);
    712     rv = tp->tp_tx(pkt->unit, pkt, nh_cookie);
    713 
    714     if (rv != BCM_E_NONE || callback == NULL) {
    715         cputrans_tx_pkt_list_free(pkt);
    716     }
    717 
    718     return rv;
    719 }
    720 
    721 
    722 /*
    723  * Function:
    724  *      nh_pkt_setup
    725  * Purpose:
    726  *      Set up a packet or list of packets to be sent using nh_pkt_tx
    727  * Parameters:
    728  *      pkt         - Pointer to the packet (or list of packets) to setup
    729  *      cos         - COS and internal priority on which to send packet
    730  *      vlan        - VLAN on which to send packet
    731  *      pkt_type    - For multiplexing on RX end.
    732  *      ct_flags    - Only CPUTRANS_NO_L2_UPDATE applies
    733  * Notes:
    734  *      This is a preliminary setup function used before the
    735  *      actual (unit, port) is known.
    736  *
    737  *      If !(ct_flags & CPUTRANS_NO_L2_UPDATE), then the L2 header
    738  *      will be updated meaning that the L2 dest MAC address will be set
    739  *      to the broadcast address.
    740  *
    741  *      The 'cos' parameter contains the cos and internal priority
    742  *      values encoded.  If the internal priority valid bit
    743  *      CPUTRANS_INT_PRIO_VALID is not set, use the cos value for
    744  *      internal priority.  Use CPUTRANS_COS_SET() to set cos value and
    745  *      CPUTRANS_INT_PRIO_SET() to set internal priority.
    746  */
    747 
    748 void
    749 nh_pkt_setup(bcm_pkt_t *pkt, int cos, uint16 vlan,
    750              uint16 pkt_type, uint32 ct_flags)
    751 {
    752     bcm_pkt_t *cur_pkt;
    753 
    754     for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) {
    755         _nh_pkt_setup(cur_pkt, cos, vlan, pkt_type, ct_flags);
    756         cur_pkt->call_back = NULL;
    757     }
    758 }
    759 
    760 /*
    761  * Function:
    762  *      nh_pkt_port_setup
    763  * Purpose:
    764  *      Set local info in the packet based on the TX (unit, port)
    765  * Parameters:
    766  *      pkt         - Pointer to the packet (or list of packets) to setup
    767  *      unit        - Device on which packet will be sent
    768  *      port        - Port on unit on which packet will be sent
    769  */
    770 
    771 void
    772 nh_pkt_local_setup(bcm_pkt_t *pkt, int unit, int port)
    773 {
    774     bcm_pkt_t *cur_pkt;
    775 
    776     for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) {
    777         _nh_pkt_local_setup(cur_pkt, unit, port, TRUE);
    778     }
    779 }
    780 
    781 
    782 /*
    783  * Function:
    784  *      nh_pkt_final_setup
    785  * Purpose:
    786  *      Set final info in the packet based on the TX (unit, port)
    787  * Parameters:
    788  *      pkt         - Pointer to the packet (or list of packets) to setup
    789  *      unit        - Device on which packet will be sent
    790  *      port        - Port on unit on which packet will be sent
    791  * Notes:
    792  *      This is used when the packet is being forwarded and so
    793  * the next hop info is not updated (to keep the original source
    794  * info accurate) but, e.g., the SL tag or HG tag may need updating.
    795  */
    796 
    797 void
    798 nh_pkt_final_setup(bcm_pkt_t *pkt, int unit, int port)
    799 {
    800     bcm_pkt_t *cur_pkt;
    801 
    802     for (cur_pkt = pkt; cur_pkt != NULL; cur_pkt = cur_pkt->next) {
    803         _nh_pkt_local_setup(cur_pkt, unit, port, FALSE);
    804     }
    805 }
    806 
    807 /*
    808  * Function:
    809  *      nh_pkt_tx
    810  * Purpose:
    811  *      Transmit a next hop packet from a bcm_pkt_t structure
    812  * Parameters:
    813  *      pkt        - Pointer to packet or linked list of packets to send
    814  *      callback   - If non-null, send async and make callback
    815  *      cookie     - Cookie for callback.
    816  * Returns:
    817  *      BCM_E_XXX
    818  * Notes:
    819  *      The caller MUST provide NH_TX_DATA_OFFSET bytes at the beginning
    820  *      of first data block of each packet in the list for the header.
    821  *
    822  *      The pkt->next pointer must be NULL if this is a single packet.
    823  *      The application is responsible for freeing the packet.
    824  *
    825  *      It is assumed that nh_pkt_setup has been called on the pkt or list.
    826  *      HOWEVER the source mod id/port will be remapped (if set up)
    827  *      according to the stack port on which the packet is being transmitted.
    828  */
    829 
    830 int
    831 nh_pkt_tx(bcm_pkt_t *pkt, nh_tx_cb_f callback, void *cookie)
    832 {
    833     bcm_pkt_cb_f local_cb;
    834     bcm_trans_ptr_t *tp;
    835     nh_cb_cookie_t *nh_cookie = NULL;
    836 
    837     if (!setup_done) {
    838         return BCM_E_INIT;
    839     }
    840 
    841     local_cb = NULL;
    842     if (callback != NULL) {
    843         local_cb = nh_tx_callback_nofree;
    844         nh_cookie = _nh_callback_cookie_set(callback);
    845     }
    846 
    847     pkt_list_src_mod_id_port_set(pkt);
    848     tp = resolve_pkt_trans_ptr(pkt);
    849     return tp->tp_tx_list(pkt->unit, pkt, local_cb, nh_cookie);
    850 }
    851 
    852 /* Call back from TX layer for async transmits */
    853 STATIC void
    854 nh_tx_callback(int unit, bcm_pkt_t *pkt, void *cookie)
    855 {
    856     nh_tx_cb_f callback;
    857 
    858     _nh_callback_cookie_get(cookie, &callback);
    859     if (callback) {
    860         callback(unit, pkt->rx_port, pkt->pkt_data[0].data, pkt->pkt_len,
    861                  pkt->cookie);
    862     }
    863 
    864     cputrans_tx_pkt_free(pkt);
    865 }
    866 
    867 /* Like above, but don't free the packet */
    868 STATIC void
    869 nh_tx_callback_nofree(int unit, bcm_pkt_t *pkt, void *cookie)
    870 {
    871     nh_tx_cb_f callback;
    872 
    873     _nh_callback_cookie_get(cookie, &callback);
    874     if (callback) {
    875         callback(unit, pkt->rx_port, pkt->pkt_data[0].data, pkt->pkt_len,
    876                  pkt->cookie);
    877     }
    878 }
    879 
    880 /*
    881  * Function:
    882  *      nh_tx_pkt_recognize
    883  * Purpose:
    884  *      Boolean:  Check if packet is recognized as next hop pkt
    885  * Parameters:
    886  *      pkt_buf    - The start of the packet, from L2 header
    887  *      local_type - (OUT) Returns local packet type here
    888  * Returns:
    889  *      Boolean:  TRUE if packet is next hop packet.
    890  * Notes:
    891  *      Does not check source MAC or VLAN.
    892  */
    893 
    894 int
    895 nh_tx_pkt_recognize(uint8 *pkt_buf, uint16 *pkt_type)
    896 {
    897     uint16 val16;
    898 
    899     if (sal_memcmp(pkt_buf, nh_dest_mac, sizeof(bcm_mac_t))) {
    900         LOG_DEBUG(BSL_LS_TKS_NH,
    901                   (BSL_META("NHTX: dest_mac not recognized\n")));
    902         return FALSE;
    903     }
    904 
    905     if (sal_memcmp(&pkt_buf[CPUTRANS_SNAP_OFS], nh_snap_mac,
    906                    sizeof(bcm_mac_t))) {
    907         LOG_DEBUG(BSL_LS_TKS_NH,
    908                   (BSL_META("NHTX: snap_mac not recognized\n")));
    909         return FALSE;
    910     }
    911 
    912     UNPACK_SHORT(&pkt_buf[CPUTRANS_SNAP_TYPE_OFS], val16);
    913     if (val16 != nh_snap_type) {
    914         LOG_DEBUG(BSL_LS_TKS_NH,
    915                   (BSL_META("NHTX: snap_type not recognized\n")));
    916         return FALSE;
    917     }
    918 
    919     UNPACK_SHORT(&pkt_buf[CPUTRANS_TR_TYPE_OFS], val16);
    920     if (val16 != nh_local_type) {
    921         LOG_DEBUG(BSL_LS_TKS_NH,
    922                   (BSL_META("NHTX: Local NH type %d mismatch %d\n"),
    923                    nh_local_type, val16));
    924         return FALSE;
    925     }
    926 
    927     /* Looks good, extract the packet type */
    928     UNPACK_SHORT(&pkt_buf[CPUTRANS_TR_MULT_OFS], val16);
    929     *pkt_type = val16;
    930 
    931     return TRUE;
    932 }
    933 
    934 
    935 
    936 /*
    937  * Function:
    938  *      nh_tx_reset
    939  * Purpose:
    940  *      Reset the next hop layer, freeing allocated structures
    941  * Parameters:
    942  *      reset_to_defaults   - Boolean; if TRUE, will also reset
    943  *                            parameters to default values
    944  * Returns:
    945  *      BCM_E_XXX
    946  * Notes:
    947  */
    948 
    949 int
    950 nh_tx_reset(int reset_to_defaults)
    951 {
    952     if (nh_mutex == NULL) {
    953         return BCM_E_INIT;
    954     }
    955 
    956     NH_LOCK;
    957     if (pending_count) {
    958         sal_usleep(100000);
    959         if (pending_count) {
    960             LOG_INFO(BSL_LS_TKS_NH,
    961                      (BSL_META("NHTX: %d Packets still pending on reset\n"),
    962                       pending_count));
    963             NH_UNLOCK;
    964             return BCM_E_FAIL;
    965         }
    966     }
    967 
    968     if (reset_to_defaults) {
    969         sal_memcpy(nh_dest_mac, nh_dest_mac_default, sizeof(bcm_mac_t));
    970         sal_memcpy(nh_snap_mac, nh_snap_mac_default, sizeof(bcm_mac_t));
    971 
    972         nh_snap_type          = NH_TX_SNAP_TYPE_DEFAULT;
    973         nh_local_type         = NH_TX_LOCAL_PKT_TYPE;
    974         nh_dest_port          = NH_TX_DEST_PORT_DEFAULT;
    975         nh_dest_mod           = NH_TX_DEST_MOD_DEFAULT;
    976         nh_src_port           = NH_TX_SRC_PORT_DEFAULT;
    977         nh_src_mod            = NH_TX_SRC_MOD_DEFAULT;
    978     }
    979     NH_UNLOCK;
    980 
    981     return BCM_E_NONE;
    982 }
    983 
    984 
    985 /*
    986  * Function:
    987  *      nh_tx_local_mac_set
    988  * Purpose:
    989  *      Set the local mac address used for source
    990  * Parameters:
    991  *      new_mac   - MAC to use
    992  * Returns:
    993  *      BCM_E_XXX
    994  * Notes:
    995  */
    996 
    997 int
    998 nh_tx_local_mac_set(const bcm_mac_t new_mac)
    999 {
   1000     sal_memcpy(nh_local_mac, new_mac, sizeof(bcm_mac_t));
   1001 
   1002     return BCM_E_NONE;
   1003 }
   1004 
   1005 
   1006 /*
   1007  * Function:
   1008  *      nh_tx_local_mac_get
   1009  * Purpose:
   1010  *      Get the local mac address used for source
   1011  * Parameters:
   1012  *      local_mac   - (OUT) Local MAC being used
   1013  * Returns:
   1014  *      BCM_E_XXX
   1015  * Notes:
   1016  */
   1017 
   1018 int
   1019 nh_tx_local_mac_get(bcm_mac_t *local_mac)
   1020 {
   1021     sal_memcpy(local_mac, nh_local_mac, sizeof(bcm_mac_t));
   1022 
   1023     return BCM_E_NONE;
   1024 }
   1025 
   1026 
   1027 /*
   1028  * Function:
   1029  *      nh_tx_snap_set
   1030  * Purpose:
   1031  *      Set the SNAP mac and type ID
   1032  * Parameters:
   1033  *      new_mac   - MAC to use
   1034  *      new_type  - Type ID to use; host order
   1035  * Returns:
   1036  *      BCM_E_XXX
   1037  * Notes:
   1038  *      It defaults to Broadcom, so generally not required
   1039  */
   1040 
   1041 int
   1042 nh_tx_snap_set(const bcm_mac_t new_mac, uint16 new_type,
   1043                uint16 new_local_type)
   1044 {
   1045     sal_memcpy(nh_snap_mac, new_mac, sizeof(bcm_mac_t));
   1046     nh_snap_type = new_type;
   1047     nh_local_type = new_local_type;
   1048 
   1049     return BCM_E_NONE;
   1050 }
   1051 
   1052 
   1053 /*
   1054  * Function:
   1055  *      nh_tx_snap_get
   1056  * Purpose:
   1057  *      Get the SNAP mac and type ID
   1058  * Parameters:
   1059  *      snap_mac   - (OUT) MAC to use
   1060  *      snap_type  - (OUT) Type ID to use; host order
   1061  * Returns:
   1062  *      BCM_E_XXX
   1063  * Notes:
   1064  */
   1065 
   1066 int
   1067 nh_tx_snap_get(bcm_mac_t snap_mac, uint16 *snap_type, uint16 *local_type)
   1068 {
   1069     sal_memcpy(snap_mac, nh_snap_mac, sizeof(bcm_mac_t));
   1070     *snap_type = nh_snap_type;
   1071     *local_type = nh_local_type;
   1072 
   1073     return BCM_E_NONE;
   1074 }
   1075 
   1076 /*
   1077  * Function:
   1078  *      nh_tx_unknown_modid_get
   1079  * Purpose:
   1080  *      Gets nh_tx_unknown_modid
   1081  * Parameters:
   1082  *      modid  - (OUT) nh_tx_unknown_modid value
   1083  */
   1084 int
   1085 nh_tx_unknown_modid_get(int *modid)
   1086 {
   1087     *modid = nh_tx_unknown_modid;
   1088     return BCM_E_NONE;
   1089 }
   1090 
   1091 /*
   1092  * Function:
   1093  *      nh_tx_unknown_modid_set
   1094  * Purpose:
   1095  *      Sets nh_tx_unknown_modid
   1096  * Parameters:
   1097  *      modid  - Modid to use as nh_tx_unknown_modid
   1098  */
   1099 int
   1100 nh_tx_unknown_modid_set(int modid)
   1101 {
   1102     nh_tx_unknown_modid = modid;
   1103     return BCM_E_NONE;
   1104 }
   1105 
   1106 /*
   1107  * Function:
   1108  *      nh_tx_l2_header_setup
   1109  * Purpose:
   1110  *      Set up the L2 header for next hop packet
   1111  * Parameters:
   1112  *      pkt_buf     - The L2 header buffer to set up
   1113  */
   1114 
   1115 void
   1116 nh_tx_l2_header_setup(uint8 *pkt_buf, uint16 vlan)
   1117 {
   1118     _nh_tx_l2_header_setup(pkt_buf, vlan);
   1119 }
   1120 
   1121 
   1122 /*
   1123  * Function:
   1124  *      _nh_tx_l2_header_setup
   1125  * Purpose:
   1126  *      See above; 
   1127  * Parameters:
   1128  *      pkt_buf     - The L2 header buffer to set up
   1129  */
   1130 
   1131 STATIC void
   1132 _nh_tx_l2_header_setup(uint8 *pkt_buf, uint16 vlan)
   1133 {
   1134     /* Set up the L2 header in the packet */
   1135     sal_memcpy(&pkt_buf[CPUTRANS_DMAC_OFS], nh_dest_mac, sizeof(bcm_mac_t));
   1136     sal_memcpy(&pkt_buf[CPUTRANS_SMAC_OFS], nh_local_mac, sizeof(bcm_mac_t));
   1137     PACK_SHORT(&pkt_buf[CPUTRANS_VTAG_OFS], 0x8100);
   1138     PACK_SHORT(&pkt_buf[CPUTRANS_VTAG_OFS + sizeof(uint16)], vlan);
   1139     /* Type/len field is skipped */
   1140     sal_memcpy(&pkt_buf[CPUTRANS_SNAP_OFS], nh_snap_mac, sizeof(bcm_mac_t));
   1141     PACK_SHORT(&pkt_buf[CPUTRANS_SNAP_TYPE_OFS], nh_snap_type);
   1142     PACK_SHORT(&pkt_buf[CPUTRANS_TR_TYPE_OFS], nh_local_type);
   1143 }
   1144 
   1145 
   1146 /* Initialize next hop layer. Can only be called once, so use NH_INIT macro */
   1147 
   1148 STATIC int
   1149 nh_init(void)
   1150 {
   1151     int i;
   1152 
   1153     if (nh_mutex == NULL) {
   1154         nh_mutex = sal_mutex_create("nh_tx_mutex");
   1155         if (nh_mutex == NULL) {
   1156             return BCM_E_MEMORY;
   1157         }
   1158     }
   1159 
   1160     NH_LOCK;
   1161     /* Initialize stack port structures */
   1162     for (i = 0; i < STK_PORTS_MAX; i++) {
   1163         nh_stk_ports[i].trans_ptr = NULL;
   1164         nh_stk_ports[i].unit = -1;
   1165         nh_stk_ports[i].port = -1;
   1166     }
   1167 
   1168     init_done = TRUE;
   1169     NH_UNLOCK;
   1170     return BCM_E_NONE;
   1171 }