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

tx.c (20639B)


      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:        tx.c
      8  * Purpose:
      9  * Requires:
     10  *
     11  * Notes:  Does not yet support packet gather at all.
     12  *    Still issues with data buffer transfer
     13  */
     14 
     15 #include <shared/bsl.h>
     16 
     17 #include <sal/core/libc.h>
     18 
     19 #include <bcm/types.h>
     20 #include <bcm/tx.h>
     21 #include <bcm/pkt.h>
     22 #include <bcm/error.h>
     23 #include <bcm/topo.h>
     24 #include <bcm_int/control.h>
     25 
     26 #include <bcmx/tx.h>
     27 #include <bcmx/l2.h>
     28 #include <bcmx/vlan.h>
     29 #include <bcmx/bcmx.h>
     30 #include <bcmx/trunk.h>
     31 
     32 #include "bcmx_int.h"
     33 
     34 #if defined(BROADCOM_DEBUG)
     35 #endif /* BROADCOM_DEBUG */
     36 
     37 #define BCMX_TX_INIT_CHECK    BCMX_READY_CHECK
     38 
     39 /*
     40  * For now, flood on unit 0.  This will support systems
     41  * where unit 0 is a 567x such as white knight and lancelot.
     42  */
     43 
     44 int _bcmx_tx_unit = 0;
     45 int _bcmx_tx_flood_unit = 0;
     46 
     47 /* Actions based on L2 map, etc. */
     48 
     49 #define _BCMX_TX_UC           1    /* Unicast based on given LPort */
     50 #define _BCMX_TX_FLOOD        2    /* Flood the packet on its vlan */
     51 #define _BCMX_TX_DISCARD      3    /* Discard the packet */
     52 #define _BCMX_TX_BCM          4    /* Send using bcm_tx */
     53 #define _BCMX_TX_MCAST        5    /* L2 multicast; not implemented */
     54 
     55 #define	_BCMX_UNPACK_U16(_buf, _var) \
     56 		_var  = *_buf++ << 8; \
     57 		_var |= *_buf++; 
     58 
     59 /****************************************************************
     60  *
     61  * Internal static functions
     62  */
     63 
     64 /* Get the VID from a bcm_pkt structure */
     65 STATIC INLINE bcm_vlan_t
     66 pkt_vid_get(bcm_pkt_t *pkt)
     67 {
     68     int vblk = 0;
     69     int voffset = 12;
     70     uint16 vtag;
     71     uint8 *ptr;
     72 
     73     /* First, find offset of vlan tag */
     74     if (pkt->pkt_data[0].len < 16) {
     75         if (pkt->pkt_data[1].len < 10) {
     76             vblk = 2;
     77             voffset = 0;
     78         } else {
     79             vblk = 1;
     80             voffset = 6;
     81         }
     82     }
     83 
     84     ptr = &pkt->pkt_data[vblk].data[voffset + 2];
     85     _BCMX_UNPACK_U16(ptr, vtag);
     86 
     87     return BCM_VLAN_CTRL_ID(vtag);
     88 }
     89 
     90 /* Get protocol tag from the packet */
     91 STATIC INLINE uint16
     92 pkt_tag_proto_get(bcm_pkt_t *pkt)
     93 {
     94     int vblk = 0;
     95     int voffset = 12;
     96     uint16 vtag;
     97     uint8 *ptr;
     98 
     99     /* First, find offset of vlan tag */
    100     if (pkt->pkt_data[0].len < 16) {
    101         if (pkt->pkt_data[1].len < 10) {
    102             vblk = 2;
    103             voffset = 0;
    104         } else {
    105             vblk = 1;
    106             voffset = 6;
    107         }
    108     }
    109 
    110     ptr = &pkt->pkt_data[vblk].data[voffset];
    111     _BCMX_UNPACK_U16(ptr, vtag);
    112 
    113     return vtag;
    114 }
    115 
    116 /* Get the VID of the packet if tagged, or default VID */
    117 STATIC INLINE bcm_vlan_t
    118 pkt_vid_resolve(bcm_pkt_t *pkt)
    119 {
    120     bcm_vlan_t vid = 1;
    121 
    122     if (pkt_tag_proto_get(pkt) == 0x8100) { /* pkt is tagged */
    123         vid = pkt_vid_get(pkt);
    124     } else {
    125         bcmx_vlan_default_get(&vid);
    126     }
    127 
    128     return vid;
    129 }
    130 
    131 
    132 /*
    133  * Check if packet needs L2 lookup and carry out if it does.
    134  * Returns BCMX_TX_ return values OK, FLOODED, DISCARDED.
    135  *
    136  * TO DO:  Implement multicast support.
    137  */
    138 
    139 STATIC INLINE int
    140 pkt_addr_resolve(bcm_pkt_t *pkt, uint32 flags, bcmx_lport_t *d_port)
    141 {
    142     bcm_vlan_t vid;
    143     bcmx_l2_addr_t l2addr;
    144     int rv = BCM_E_NONE;
    145 
    146     if (flags & BCMX_TX_F_FLOOD_PKT) {
    147         return _BCMX_TX_FLOOD;
    148     }
    149 
    150     /* Check if L2 lookup required */
    151     if (flags & BCMX_TX_F_L2_LOOKUP) {
    152         *d_port = BCMX_LPORT_INVALID;
    153 
    154         if ((pkt->pkt_data[0].data[0] & 0x1) == 0) {
    155             /* Only look up unicast packets */
    156             vid = pkt_vid_resolve(pkt);
    157 
    158             sal_memset(&l2addr, 0, sizeof(l2addr));
    159             if (bcmx_l2_addr_get((void *)pkt->pkt_data[0].data,
    160                                  vid, &l2addr, NULL) == 0) {
    161                 if (l2addr.flags & BCM_L2_TRUNK_MEMBER) {
    162                     bcmx_trunk_add_info_t t_data;
    163 
    164                     if (bcmx_trunk_get(l2addr.tgid, &t_data) >= 0) {
    165                         bcmx_lport_t lport;
    166                         int count, local_tx = 0, unit = 0;
    167 
    168                         if (BCMX_LPLIST_IS_EMPTY(&t_data.ports)) {
    169                             /*
    170                              * L2 table is programmed with tgid, but
    171                              * no trunk members found
    172                              */
    173                             rv = BCM_E_INTERNAL;
    174                         }
    175 
    176                         if(BCM_SUCCESS(rv)) {
    177                             BCMX_LPLIST_IDX_ITER(&t_data.ports, lport, count) {
    178                                 unit = bcmx_lport_bcm_unit(lport);
    179 
    180                                 if (unit < 0) {
    181                                     rv = BCM_E_PORT;
    182                                     break;
    183                                 }
    184 
    185                                 if ((BCM_SUCCESS(rv)) && (BCM_IS_LOCAL(unit))) {
    186                                     local_tx = 1;
    187                                     *d_port = lport;
    188                                     break;
    189                                 }
    190                             }
    191                         }
    192 
    193                         if (BCM_SUCCESS(rv) && (!local_tx)) {
    194                             *d_port = bcmx_lplist_first(&t_data.ports);
    195                         }
    196                     } else {
    197                         /*
    198                          * bcmx_trunk_get failed; Non-existent tid probably
    199                          */
    200                         rv = BCM_E_INTERNAL;
    201                     }
    202                     bcmx_trunk_add_info_t_free(&t_data);
    203                     if(BCM_FAILURE(rv)) {
    204                         return rv;
    205                     }
    206                 } else {
    207                     /* l2addr.port is overloaded with logical port */
    208                     *d_port = l2addr.lport;
    209                 }
    210             }
    211         } else {
    212             return BCM_E_UNAVAIL;
    213         }
    214 
    215         if (*d_port == BCMX_LPORT_INVALID) {
    216             if (pkt->flags & BCMX_TX_F_DLF_DISCARD) {
    217                 return _BCMX_TX_DISCARD;
    218             } else {
    219                 return _BCMX_TX_FLOOD;
    220             }
    221         }
    222 
    223         return _BCMX_TX_UC;
    224     }
    225 
    226     return _BCMX_TX_BCM;
    227 }
    228 
    229 /*
    230  * Function:
    231  *      bcmx_tx
    232  * Purpose:
    233  *      Transmit a packet according to flags
    234  * Parameters:
    235  *      pkt    - The packet to transmit
    236  *      flags  - Flags as defined in include/bcmx/tx.h
    237  * Returns:
    238  *      BCMX_E_XXX < 0 on error.
    239  * Notes:
    240  *      If the L2 map flag is set, an L2 LOOKUP is performed on
    241  * the packet.  If this fails and the DLF_DISCARD flag is set
    242  * then the packet is not transmitted.  Otherwise it is flooded
    243  * to the packet's VLAN.  This is indicated by the return value
    244  * (see include/bcmx/tx.h).
    245  *
    246  *      If the L2 map flag is not set, the packet is sent on the
    247  * unit given by pkt->unit with the port bitmaps given by
    248  * pkt->tx_pbmp, etc.
    249  *
    250  *      The semantics match those of bcm_tx.  In particular,
    251  * the packet callback being non-null indicates a user callback
    252  * should be made and the transmit will be done asynchronously.
    253  *
    254  *      The packet's L3 and untagged bitmaps are AND-ed with the
    255  * tx bitmap.  The caller can use bcmx_tx_pkt_l3_set or
    256  * bcmx_tx_pkt_untagged_set to set all ports in the bitmap
    257  */
    258 
    259 int
    260 bcmx_tx(bcm_pkt_t *pkt, uint32 flags)
    261 {
    262     bcmx_lport_t d_port;
    263     int rv;
    264 
    265     BCMX_TX_INIT_CHECK;
    266 
    267     if (pkt == NULL || pkt->pkt_data == NULL || pkt->blk_count <= 0) {
    268         return BCM_E_PARAM;
    269     }
    270 
    271     /* Try to L2 map if needed; if pkt discarded, return */
    272     d_port = BCMX_LPORT_INVALID;
    273     switch ((rv = pkt_addr_resolve(pkt, flags, &d_port))) {
    274     case _BCMX_TX_UC:  /* Okay */
    275         return bcmx_tx_uc(pkt, d_port, flags);
    276 
    277     case _BCMX_TX_FLOOD:  /* Packet to be flooded */
    278         return bcmx_tx_flood(pkt, flags);
    279 
    280     case _BCMX_TX_DISCARD:  /* Discarded */
    281         return BCMX_TX_DISCARDED;
    282 
    283         /* TO DO:  Implement multicast support here */
    284     case _BCMX_TX_BCM:
    285         return bcm_tx(pkt->unit, pkt, NULL);
    286 
    287     case _BCMX_TX_MCAST:
    288         return BCM_E_UNAVAIL;   /* Not yet implemented */
    289 
    290     default:
    291         /* Error */
    292         LOG_WARN(BSL_LS_BCMX_COMMON,
    293                  (BSL_META("bcmx_tx: packet resolve error (%d): %s\n"),
    294                   rv, bcm_errmsg(rv)));
    295         break;
    296     }
    297 
    298     /* Error if we get here */
    299     return rv;
    300 }
    301 
    302 
    303 /*
    304  * Function:
    305  *      bcmx_tx_uc
    306  * Purpose:
    307  *      Transmit a packet out a specific front panel port
    308  * Parameters:
    309  *      pkt    - The packet to transmit
    310  *      d_port - The front panel destination port
    311  * Returns:
    312  *      BCM_E_XXX
    313  * Notes:
    314  *      The semantics match those of bcm_tx.  In particular,
    315  * the packet callback being non-null indicates a user callback
    316  * should be made and the transmit will be done asynchronously.
    317  *
    318  *      The packet's L3 and untagged bitmaps are AND-ed with the
    319  * tx bitmap.  The caller can use bcmx_tx_pkt_l3_set or
    320  * bcmx_tx_pkt_untagged_set to set all ports in the bitmap
    321  */
    322 
    323 int
    324 bcmx_tx_uc(bcm_pkt_t *pkt, bcmx_lport_t d_port, uint32 flags)
    325 {
    326     int          rv = BCM_E_NONE;
    327     bcm_port_t   port, modport;
    328     int          unit;
    329     bcm_module_t modid;
    330 
    331     BCMX_TX_INIT_CHECK;
    332 
    333     if (pkt == NULL || pkt->pkt_data == NULL || pkt->blk_count <= 0) {
    334         return BCM_E_PARAM;
    335     }
    336 
    337     BCM_IF_ERROR_RETURN
    338         (_bcmx_dest_to_unit_port(d_port, &unit, &port,
    339                                  BCMX_DEST_CONVERT_NON_GPORT));
    340     BCM_IF_ERROR_RETURN
    341         (_bcmx_dest_to_modid_port(d_port, &modid, &modport,
    342                                   BCMX_DEST_CONVERT_DEFAULT));
    343 
    344     /* First, try to send the packet on a local unit, directing
    345      * the packet using the module ID.
    346      */
    347     if (modid < 0) { /* Bad modid */
    348         return BCM_E_BADID;
    349     }
    350 
    351     if (!BCM_UNIT_VALID(unit)) {
    352         return BCM_E_UNIT;
    353     }
    354 
    355     pkt->dest_mod = modid;
    356     pkt->dest_port = modport;
    357     pkt->opcode = BCM_HG_OPCODE_UC;
    358 
    359     if (!BCM_IS_LOCAL(unit) && BCM_IS_LOCAL(_bcmx_tx_unit)) {
    360         /* Try to send directly; if unable to map, tunnel via bcm_tx. */
    361         bcm_port_t local_tx_port;
    362 
    363         rv = bcm_topo_port_get(_bcmx_tx_unit, (int)modid, &local_tx_port);
    364         if (!(flags & BCMX_TX_F_CPU_TUNNEL) && (rv == BCM_E_NONE) &&
    365             (local_tx_port != -1)) {
    366             /* Can transmit locally to port; otherwise tunneled */
    367             unit = _bcmx_tx_unit;
    368             port = local_tx_port;
    369         }
    370     }
    371 
    372     BCM_PBMP_PORT_SET(pkt->tx_pbmp, port);
    373     pkt->unit = unit;
    374     return bcm_tx(unit, pkt, NULL);
    375 }
    376 
    377 
    378 /*
    379  * Function:
    380  *      bcmx_tx_lplist
    381  * Purpose:
    382  *      Transmit a packet to a list of ports (efficiently)
    383  * Parameters:
    384  *      pkt             -- Pointer to packet to send
    385  *      tx_ports        -- List of ports to which to send; if NULL,
    386  *                         use untagged port list only
    387  *      untagged_ports  -- Egress ports which should be untagged
    388  *      flags           -- For future enhancements (unused)
    389  * Returns:
    390  *      BCM_E_XXX
    391  * Notes:
    392  *      Packet will be sent tagged unless:
    393  *           tx_ports is NULL/empty, in which case the packet is sent
    394  *           out untagged on all ports specified in the untagged_ports list;
    395  *           OR both tx_ports and untagged_ports params are non-NULL,
    396  *           in which case the packet is sent out untagged on the ports
    397  *           specified in untagged_ports.  If both are specified,
    398  *           untagged_ports must be a subset of tx_ports.
    399  *
    400  *      The port list is scanned first and the devices that appear
    401  *      on the list are extracted.  The code then iterates over
    402  *      those units and creates a bitmap for each unit.
    403  *
    404  *      Async transmit is supported, but it won't really take place
    405  *      until the last unit transmit is set up.
    406  */
    407 
    408 int
    409 bcmx_tx_lplist(bcm_pkt_t *pkt, bcmx_lplist_t *tx_ports,
    410                bcmx_lplist_t *untagged_ports, uint32 flags)
    411 {
    412     bcm_unit_bmp_t tx_units;
    413     int unit;
    414     bcmx_lport_t lport;
    415     bcmx_lport_t cb_lport = BCMX_LPORT_INVALID;
    416     int i;
    417     bcm_pbmp_t tx_pbmp, ut_pbmp;
    418     int rv, final_rv = BCM_E_NONE;
    419     int unit_iter=0, unit_count = 0;
    420     bcm_pkt_cb_f pkt_callback = NULL;
    421     int all_untagged = FALSE;
    422     int local_unit_found = FALSE;
    423     int bcm_unit;
    424     bcm_port_t bcm_port;
    425 
    426     BCMX_TX_INIT_CHECK;
    427 
    428     if (tx_ports == NULL || BCMX_LPLIST_IS_EMPTY(tx_ports)) {
    429         if (untagged_ports == NULL || BCMX_LPLIST_IS_EMPTY(untagged_ports)) {
    430             LOG_VERBOSE(BSL_LS_BCMX_COMMON,
    431                         (BSL_META("bcmx_tx_lplist: No TX ports given\n")));
    432             return BCM_E_NONE;
    433         }
    434         all_untagged = TRUE;
    435         tx_ports = untagged_ports;
    436     }
    437 
    438     /* Only set callback on transmit of last packet.  Note that
    439      * we aren't really sending async until transmitting to the
    440      * last unit.
    441      */
    442     pkt_callback = pkt->call_back;
    443     pkt->call_back = NULL;
    444 
    445     /* Find which units being transmitted to, and which remote lport
    446        may require a callback.
    447 
    448        Only the last lport on a remote unit is eligible for a
    449        callback, and the presence of any local port will cancel such
    450        eligibility. */
    451     
    452     BCM_UNIT_BMP_CLEAR(tx_units);
    453     BCMX_LPLIST_IDX_ITER(tx_ports, lport, i) {
    454         unit = bcmx_lport_bcm_unit(lport);
    455         if (unit < 0) {
    456             /* Port got disconnected */
    457             return BCM_E_INTERNAL;
    458         }
    459 
    460         BCM_UNIT_BMP_ADD(tx_units, unit);
    461 
    462         /* See if there needs to be a callback for a remote port */
    463         if (!local_unit_found) {
    464             if (BCM_IS_LOCAL(unit)) {
    465                 /* Local port found, so discontinue looking for a
    466                    callback port */
    467                 cb_lport = BCMX_LPORT_INVALID;
    468                 local_unit_found = TRUE;
    469             } else {
    470                 cb_lport = lport;
    471             }
    472         }
    473     }
    474 
    475     if (!(flags & BCMX_TX_F_CPU_TUNNEL)) {
    476         /*
    477          * For the logical ports that reside on remote units, let us try and 
    478          * switch directly if a path is available (using bcmx_tx_uc())
    479          *
    480          * Note: the logical ports that are handled here using bcmx_tx_uc()
    481          * will be excluded from the unit iterator further below (that takes
    482          * care of logical ports residing on local units only)
    483          */
    484         BCMX_LPLIST_IDX_ITER(tx_ports, lport, i) {
    485             unit = bcmx_lport_bcm_unit(lport);
    486             if (unit < 0) {
    487                 return BCM_E_INTERNAL;
    488             }
    489             if (BCM_IS_LOCAL(unit)) {
    490                 continue;
    491             }
    492 
    493             if (BCM_UNIT_BMP_TEST(tx_units, unit)) {
    494                 BCM_UNIT_BMP_REMOVE(tx_units, unit);
    495             }
    496 
    497             if (lport == cb_lport) {
    498                 pkt->call_back = pkt_callback;
    499             }
    500 
    501             rv = bcmx_tx_uc(pkt, lport, flags);
    502             if (rv < 0) {
    503                 LOG_ERROR(BSL_LS_BCMX_COMMON,
    504                           (BSL_META_U(unit,
    505                                       "BCMX tx_lplist: Failed on unit %d port 0x%x: %s\n"),
    506                            unit, lport, bcm_errmsg(rv)));
    507                 final_rv = rv;
    508             }
    509         }
    510 
    511     }
    512 
    513     /* Count the units for setting up callback if present */
    514     for (unit = 0; unit < BCM_UNITS_MAX; unit++) {
    515         if (BCM_UNIT_BMP_TEST(tx_units, unit)) {
    516             ++unit_count;
    517         }
    518     }
    519 
    520     /* Send out packet on units */
    521     for (unit = 0; unit < BCM_UNITS_MAX; unit++) {
    522         if (BCM_UNIT_BMP_TEST(tx_units, unit)) {
    523             if (++unit_iter == unit_count) {
    524                 /* Restore callback for async operation on last pkt */
    525                 pkt->call_back = pkt_callback;
    526             }
    527 
    528             /* Generate the transmit bitmaps for this device */
    529             BCM_PBMP_CLEAR(tx_pbmp);
    530             BCM_PBMP_CLEAR(ut_pbmp);
    531             BCMX_LPLIST_IDX_ITER(tx_ports, lport, i) {
    532                 if (BCM_SUCCESS
    533                     (_bcmx_dest_to_unit_port(lport, &bcm_unit, &bcm_port,
    534                                              BCMX_DEST_CONVERT_NON_GPORT))) {
    535                     if (bcm_unit == unit) {
    536                         BCM_PBMP_PORT_ADD(tx_pbmp, bcm_port);
    537                     }
    538                 }
    539             }
    540             if ((!all_untagged) &&
    541                 (untagged_ports != NULL) &&
    542                 (!BCMX_LPLIST_IS_EMPTY(untagged_ports))) {
    543                 BCMX_LPLIST_IDX_ITER(untagged_ports, lport, i) {
    544                     if (BCM_SUCCESS
    545                         (_bcmx_dest_to_unit_port(lport,
    546                                                  &bcm_unit, &bcm_port,
    547                                                  BCMX_DEST_CONVERT_NON_GPORT))) {
    548                         if (bcm_unit == unit) {
    549                             BCM_PBMP_PORT_ADD(ut_pbmp, bcm_port);
    550                         }
    551                     }
    552                 }
    553             }
    554 
    555             BCM_PBMP_ASSIGN(pkt->tx_pbmp, tx_pbmp);
    556             if (all_untagged) {
    557                 BCM_PBMP_ASSIGN(pkt->tx_upbmp, tx_pbmp);
    558             } else {
    559                 BCM_PBMP_ASSIGN(pkt->tx_upbmp, ut_pbmp);
    560             }
    561             pkt->unit = unit;
    562             rv = bcm_tx(unit, pkt, NULL);
    563 
    564             if (rv < 0) {
    565                 LOG_ERROR(BSL_LS_BCMX_COMMON,
    566                           (BSL_META_U(unit,
    567                                       "BCMX tx_lplist: Failed on unit %d:%s\n"),
    568                            unit, bcm_errmsg(rv)));
    569                 final_rv = rv;
    570             }
    571         }
    572     }
    573 
    574     return final_rv;
    575 
    576 }
    577 
    578 
    579 /*
    580  * Function:
    581  *      bcmx_tx_flood
    582  * Purpose:
    583  *      Flood a packet on its VLAN
    584  * Parameters:
    585  *      pkt    - The packet to transmit
    586  * Returns:
    587  *      BCM_E_XXX
    588  * Notes:
    589  *      The CRC field in pkt->flags must be set before calling.
    590  */
    591 
    592 int
    593 bcmx_tx_flood(bcm_pkt_t *pkt, uint32 flags)
    594 {
    595     int rv;
    596     bcm_port_config_t config;
    597 
    598     COMPILER_REFERENCE(flags);
    599 
    600     BCMX_TX_INIT_CHECK;
    601 
    602     /* To do:  detect tunnel and forward to each CPU in system */
    603 
    604     if (pkt == NULL || pkt->pkt_data == NULL || pkt->blk_count <= 0) {
    605         return BCM_E_PARAM;
    606     }
    607 
    608     rv = bcm_port_config_get(_bcmx_tx_flood_unit, &config);
    609     if (rv < 0) {
    610         return rv;
    611     }
    612 
    613     BCM_PBMP_ASSIGN(pkt->tx_pbmp, config.port);
    614     pkt->unit = _bcmx_tx_flood_unit;
    615     pkt->opcode = BCM_HG_OPCODE_BC;
    616     rv = bcm_tx(_bcmx_tx_flood_unit, pkt, NULL);
    617 
    618     if (rv < 0) {
    619         return rv;
    620     } else {
    621         return BCMX_TX_FLOODED;
    622     }
    623 }
    624 
    625 
    626 /*
    627  * Function:
    628  *      bcmx_tx_pkt_l3_set
    629  * Purpose:
    630  *      Set l3 characteristic for a bcm pkt sent thru bcmx
    631  * Parameters:
    632  *      pkt         - the pkt to affect
    633  *      l3          - boolean; should pkt be sent as l3
    634  * Returns:
    635  *      BCM_E_XXX
    636  */
    637 
    638 int
    639 bcmx_tx_pkt_l3_set(bcm_pkt_t *pkt, int l3)
    640 {
    641     bcm_pbmp_t tmp_pbm;
    642 
    643     if (pkt) {
    644         if (l3) {
    645             BCM_PBMP_CLEAR(tmp_pbm);
    646             BCM_PBMP_NEGATE(pkt->tx_l3pbmp, tmp_pbm);
    647         } else {
    648             BCM_PBMP_CLEAR(pkt->tx_l3pbmp);
    649         }
    650     }
    651 
    652     return BCM_E_NONE;
    653 }
    654 
    655 
    656 /*
    657  * Function:
    658  *      bcmx_tx_pkt_untagged_set
    659  * Purpose:
    660  *      Set untagged characteristic for a bcm pkt sent thru bcmx
    661  * Parameters:
    662  *      pkt         - the pkt to affect
    663  *      untagged    - boolean; should pkt be tagged
    664  * Returns:
    665  *      BCM_E_XXX
    666  */
    667 
    668 int
    669 bcmx_tx_pkt_untagged_set(bcm_pkt_t *pkt, int untagged)
    670 {
    671     bcm_pbmp_t tmp_pbm;
    672 
    673     if (pkt) {
    674         if (untagged) {
    675             BCM_PBMP_CLEAR(tmp_pbm);
    676             BCM_PBMP_NEGATE(pkt->tx_upbmp, tmp_pbm);
    677         } else {
    678             BCM_PBMP_CLEAR(pkt->tx_upbmp);
    679         }
    680     }
    681 
    682     return BCM_E_NONE;
    683 }
    684 
    685 
    686 
    687 /****************************************************************
    688  *
    689  * The following calls are DEPRECATED.
    690  */
    691 
    692 /*
    693  * Function:
    694  *      bcmx_tx_port_list   DEPRECATED:  Use bcmx_tx_lplist.
    695  * Purpose:
    696  *      Transmit a packet to a list of ports
    697  * Parameters:
    698  *      lplist    - lport list of destinations
    699  *      pkt       - pointer to packet to send
    700  * Returns:
    701  *      BCM_E_XXX
    702  * Notes:
    703  *      Returns last error seen if one occurs and continues attempting
    704  *      to send.
    705  *
    706  *      Not sophisticated yet; just sends out to each port individually.
    707  *      This could generate the bitmaps per unit and send them out
    708  *      once per unit.
    709  */
    710 
    711 int
    712 bcmx_tx_port_list(bcmx_lplist_t *lplist, bcm_pkt_t *pkt)
    713 {
    714     bcmx_lport_t lport;
    715     int count;
    716     int rv = BCM_E_NONE;
    717     int rv_keep = BCM_E_NONE;
    718 
    719     BCMX_TX_INIT_CHECK;
    720 
    721     if (pkt->call_back != NULL) {
    722         LOG_WARN(BSL_LS_BCMX_COMMON,
    723                  (BSL_META("WARNING: bcmx_tx_port_list: async not supported\n")));
    724     }
    725     BCMX_LPLIST_ITER(*lplist, lport, count) {
    726         rv = bcmx_tx_uc(pkt, lport, 0);
    727         if (rv < 0) {
    728             rv_keep = rv;
    729         }
    730     }
    731 
    732     return rv_keep;
    733 }