pkt.c (18923B)
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 8 #include <shared/alloc.h> 9 #include <sal/core/libc.h> 10 11 #include <soc/cm.h> 12 #include <soc/drv.h> 13 14 #include <bcm/error.h> 15 #include <bcm/types.h> 16 #include <bcm/rx.h> 17 #include <bcm/pkt.h> 18 #include <bcm_int/control.h> 19 #if defined(INCLUDE_L3) && defined(BCM_FIREBOLT_SUPPORT) 20 #include <bcm_int/esw/firebolt.h> 21 #include <bcm_int/esw/l3.h> 22 #endif 23 24 #define UNIT_VALID(unit) \ 25 { \ 26 if (!BCM_UNIT_VALID(unit)) { return BCM_E_UNIT; } \ 27 } 28 29 /* 30 * Function: 31 * bcm_pkt_byte_index 32 * Purpose: 33 * Return a pointer to a location in a packet 34 * Parameters: 35 * pkt - The packet of interest 36 * n - Byte offset in the packet 37 * len - (OUT) number of bytes left in this allocation block 38 * blk - (OUT) if not NULL, gets the pointer to the current block 39 * location - (OUT) pointer to the requested byte 40 * Returns: 41 * BCM_E_XXX 42 */ 43 44 int 45 bcm_pkt_byte_index(bcm_pkt_t *pkt, int n, int *len, int *blk_idx, 46 uint8 **location) 47 { 48 int cnt = 0; 49 int blk = 0; 50 uint8 *ptr = NULL; 51 int offset; 52 53 for (blk = 0; blk < pkt->blk_count; blk++) { 54 if (cnt + pkt->pkt_data[blk].len > n) { 55 offset = n - cnt; 56 ptr = &pkt->pkt_data[blk].data[offset]; 57 if (len) { 58 *len = pkt->pkt_data[blk].len - offset; 59 } 60 if (blk_idx) { 61 *blk_idx = blk; 62 } 63 break; 64 } else { 65 cnt += pkt->pkt_data[blk].len; 66 } 67 } 68 69 *location = ptr; 70 return (NULL !=ptr) ? BCM_E_NONE : BCM_E_NOT_FOUND; 71 } 72 73 74 /* 75 * Function: 76 * bcm_pkt_memcpy 77 * Purpose: 78 * 79 * Parameters: 80 * pkt - Packet to copy into 81 * dest_byte - Integer offset into packet 82 * src - Source buffer to copy from 83 * len - Number of bytes to copy 84 * Returns: 85 * Number of bytes actually copied 86 */ 87 88 int 89 bcm_pkt_memcpy(bcm_pkt_t *pkt, int dest_byte, uint8 *src, int len) 90 { 91 int blk; 92 uint8 *ptr = NULL; 93 int blk_bytes, rv; 94 int copied = 0; 95 96 rv = bcm_pkt_byte_index(pkt, dest_byte, &blk_bytes, &blk, &ptr); 97 98 if ((BCM_E_NONE == rv) && (ptr)) { 99 while (1) { 100 if (blk_bytes > 0) { 101 if (len - copied <= blk_bytes) { /* Last block */ 102 sal_memcpy(ptr, &src[copied], len - copied); 103 copied = len; 104 break; 105 } 106 sal_memcpy(ptr, &src[copied], blk_bytes); 107 copied += blk_bytes; 108 } 109 if (++blk >= pkt->blk_count) { 110 break; 111 } 112 ptr = pkt->pkt_data[blk].data; 113 blk_bytes = pkt->pkt_data[blk].len; 114 } 115 } 116 117 return copied; 118 } 119 120 /* 121 * Function: 122 * bcm_pkt_t_init 123 * Purpose: 124 * Initialize packet structure. 125 * Parameters: 126 * pkt - Pointer to packet structure. 127 * Returns: 128 * NONE 129 */ 130 void 131 bcm_pkt_t_init(bcm_pkt_t *pkt) 132 { 133 if (pkt != NULL) { 134 sal_memset(pkt, 0, sizeof (*pkt)); 135 } 136 return; 137 } 138 139 /* 140 * Function: 141 * bcm_pkt_blk_t_init 142 * Purpose: 143 * Initialize packet block structure. 144 * Parameters: 145 * pkt_blk - Pointer to packet block structure. 146 * Returns: 147 * NONE 148 */ 149 void 150 bcm_pkt_blk_t_init(bcm_pkt_blk_t *pkt_blk) 151 { 152 if (pkt_blk != NULL) { 153 sal_memset(pkt_blk, 0, sizeof (*pkt_blk)); 154 } 155 return; 156 } 157 158 /* 159 * Function: 160 * bcm_pkt_clear 161 * Purpose: 162 * Clear a packet structure and install its data buffer 163 * Parameters: 164 * unit - for what unit is this being set up 165 * pkt - pointer to pointer packet to setup. (*pkt) may be NULL 166 * blks - Pointer to array of gather blocks for the packet 167 * blk_count - Number of elts in the array 168 * flags - flags for CRC and VLAN tag. See notes. 169 * pkt_buf (OUT) - pkt, or allocated packet if pkt is NULL 170 * Returns: 171 * Pointer to packet or NULL if cannot allocate new packet. 172 * Notes: 173 * If pkt is null, allocate a new one with sal_alloc. 174 * 175 * Flags can be the bitwise or of: 176 * BCM_TX_CRC_NONE No action on CRC 177 * BCM_TX_CRC_ALLOC CRC is not in packet; allocate 178 * BCM_TX_CRC_REGEN Regenerate the CRC on egress 179 * BCM_TX_CRC_APPEND Allocate and regenerate (same as alloc) 180 * BCM_PKT_F_NO_VTAG Packet does not contain vlan tag 181 * 182 * In addition, the following may be used to override normal 183 * behavior, but are not generally advertised or documented. 184 * BCM_TX_CRC_FORCE_ERROR Force an error in the CRC (unsupported) 185 * BCM_PKT_F_HGHDR HiGig header setup for packet DMA 186 * BCM_PKT_F_SLTAG SL tag setup for packet DMA 187 * 188 * The payload length of the packet is set as large as possible 189 * assuming the allocation blocks contain the MAC addresses and 190 * (unless NO_VTAG is specified) the VLAN tag; if CRC append (or alloc) 191 * is indicated, the CRC is assumed NOT to be part of the gather 192 * blocks. 193 */ 194 195 int 196 bcm_pkt_clear(int unit, bcm_pkt_t *pkt, bcm_pkt_blk_t *blks, int blk_count, 197 uint32 flags, bcm_pkt_t **pkt_buf) 198 { 199 int rv, i; 200 int bytes = 0; 201 int local_alloc = FALSE; 202 203 UNIT_VALID(unit); 204 205 if (pkt == NULL) { /* Allocate new packet */ 206 local_alloc = TRUE; 207 if ((pkt = sal_alloc(sizeof(bcm_pkt_t), "pkt_setup")) == NULL) { 208 *pkt_buf = NULL; 209 return BCM_E_MEMORY; 210 } 211 } 212 sal_memset(pkt, 0, sizeof(bcm_pkt_t)); 213 pkt->unit = unit; 214 215 if (blk_count == 0) { 216 /* 217 * Permit allocation of an empty packet structure. 218 * A single buffer can be added alter using the 219 * BCM_PKT_ONE_BUF_SETUP macro. 220 */ 221 bcm_pkt_flags_init(unit, pkt, flags); 222 } else { 223 for (i = 0; i < blk_count; i++) { 224 bytes += blks[i].len; 225 } 226 227 if (bytes < BCM_PKT_ALLOC_MIN) { 228 *pkt_buf = NULL; 229 if (local_alloc) { 230 sal_free(pkt); 231 } 232 return BCM_E_MEMORY; 233 } 234 235 pkt->pkt_data = blks; 236 pkt->blk_count = blk_count; 237 238 rv = bcm_pkt_flags_len_setup(unit, pkt, bytes, -1, flags); 239 if (BCM_FAILURE(rv)) { 240 *pkt_buf = NULL; 241 if (local_alloc) { 242 sal_free(pkt); 243 } 244 return rv; 245 } 246 } 247 248 *pkt_buf = pkt; 249 return BCM_E_NONE; 250 } 251 252 /* 253 * Function: 254 * bcm_pkt_flags_len_setup 255 * Purpose: 256 * Install data in a packet without clearing fields 257 * Parameters: 258 * unit - for what unit is this being set up 259 * pkt - the packet to setup 260 * buf - buffer to use; must be non-null 261 * alloc_bytes - buffer length (allocated) 262 * payload_len - Length of payload. See notes 263 * flags - See above. 264 * Returns: 265 * >= 0: Payload length 266 * < 0: BCM_E_XXX 267 * Notes: 268 * If payload_len < 0, this means make payload as large as possible. 269 * 270 * The payload_len is checked because space for the VLAN tag, 271 * HiGig header and SL tag is always allocated. 272 * 273 * The payload length includes everything in the packet EXCEPT: 274 * dest and src MAC; 275 * VLAN tag (4 bytes) 276 * CRC 277 * SL Tag 278 * HiGig header 279 * The txrx length includes all of the above. 280 * The packet length value depends on the format indications. 281 * It will exclude the VLAN tag if indicated in flags; it will 282 * exclude the CRC if CRC_ALLOC is indicated. 283 * 284 * Valid values for CRC are: 285 * BCM_TX_CRC_NONE, BCM_TX_CRC_ALLOC, BCM_TX_CRC_REGEN, 286 * BCM_TX_CRC_APPEND. These maybe combined with 287 * BCM_TX_CRC_FORCE_ERROR, 288 */ 289 290 int 291 bcm_pkt_flags_len_setup(int unit, bcm_pkt_t *pkt, int alloc_bytes, 292 int payload_len, uint32 flags) 293 { 294 UNIT_VALID(unit); 295 296 if (payload_len < 0) { 297 payload_len = alloc_bytes - BCM_PKT_ALLOC_MIN; 298 } else if (payload_len > alloc_bytes - BCM_PKT_ALLOC_MIN) { 299 return BCM_E_PARAM; 300 } 301 302 bcm_pkt_flags_init(unit, pkt, flags); 303 304 return payload_len; 305 } 306 307 308 int 309 bcm_pkt_flags_init(int unit, bcm_pkt_t *pkt, uint32 flags) 310 { 311 int rv; 312 313 BCM_UNIT_BUSY(unit); 314 if (BCM_UNIT_VALID(unit)) { 315 pkt->flags = flags; 316 pkt->unit = unit; /* Set unit as well */ 317 318 if (BCM_IS_LOCAL(unit)) { 319 if (SOC_UNIT_VALID(unit) && SOC_IS_XGS12_FABRIC(unit)) { 320 pkt->flags |= BCM_PKT_F_HGHDR; 321 } 322 /* Was SL flags handling, but SL is no longer supported */ 323 } 324 rv = BCM_E_NONE; 325 326 } else { 327 rv = BCM_E_UNIT; 328 } 329 BCM_UNIT_IDLE(unit); 330 331 return rv; 332 } 333 334 335 336 337 338 /**************************************************************** 339 * 340 * Default allocate and free routines. Uses soc_cm_ shared 341 * memory routines. These use single data buffers and do 342 * runtime allocation and free. 343 */ 344 345 #define DEFAULT_ALLOCATOR_UNIT 0 346 #define NO_ALLOCATOR_UNIT -1 347 348 /* 349 Return a local unit number (or NO_ALLOCATOR_UNIT) that can allocate 350 a buffer for the given unit. 351 352 For local units, the unit returned is the unit passed. For remote 353 units, return DEFAULT_ALLOCATOR_UNIT if DEFAULT_ALLOCATOR_UNIT unit 354 itself is valid and local, otherwise return NO_ALLOCATOR_UNIT (which 355 usually means tunneling to a remote unit when there are no local 356 units, which is a valid configuration). 357 358 */ 359 360 static int 361 _bcm_pkt_allocator_unit(int unit) 362 { 363 /* allocation unit */ 364 if (BCM_IS_REMOTE(unit)) { 365 unit = (BCM_UNIT_VALID(DEFAULT_ALLOCATOR_UNIT) && 366 BCM_IS_LOCAL(DEFAULT_ALLOCATOR_UNIT)) ? 367 DEFAULT_ALLOCATOR_UNIT : NO_ALLOCATOR_UNIT; 368 } 369 370 return unit; 371 } 372 373 /* could be bcm_pkt_blk_t_alloc */ 374 375 static int 376 _bcm_pkt_data_alloc(int unit, int size, bcm_pkt_blk_t *pkt_data) 377 { 378 int aunit; 379 int rv = BCM_E_MEMORY; 380 381 /* allocation unit, which may not be the same as unit */ 382 aunit = _bcm_pkt_allocator_unit(unit); 383 384 BCM_UNIT_BUSY(aunit); 385 /* If aunit == NO_ALLOCATOR_UNIT, use a heap allocator, because 386 there are no DMAable devices available. */ 387 pkt_data->data = (aunit == NO_ALLOCATOR_UNIT) ? 388 sal_alloc(size, "pkt alloc data") : 389 soc_cm_salloc(aunit, size, "pkt alloc data"); 390 391 if (pkt_data->data) { 392 pkt_data->len = size; 393 rv = BCM_E_NONE; 394 } 395 BCM_UNIT_IDLE(aunit); 396 397 return rv; 398 } 399 400 static int 401 _bcm_pkt_data_free(int unit, bcm_pkt_blk_t *pkt_data) 402 { 403 int aunit; 404 405 /* allocation unit, which may not be the same as unit */ 406 aunit = _bcm_pkt_allocator_unit(unit); 407 BCM_UNIT_BUSY(aunit); 408 if (pkt_data->data != NULL) { 409 if (aunit == NO_ALLOCATOR_UNIT) { 410 sal_free(pkt_data->data); 411 } else { 412 soc_cm_sfree(aunit, pkt_data->data); 413 } 414 } 415 pkt_data->data = NULL; 416 pkt_data->len = 0; 417 BCM_UNIT_IDLE(aunit); 418 419 return BCM_E_NONE; 420 } 421 422 423 424 /* 425 * Function: 426 * bcm_pkt_alloc 427 * Purpose: 428 * Basic, single block packet allocation using sal and soc functions 429 * Parameters: 430 * unit - Strata unit 431 * size - Bytes to allocate 432 * flags - See pkt.h for more about these flags 433 * Returns: 434 * Pointer to packet if successful or NULL if not 435 * Notes: 436 * Flags are copied into the packet, and so may indicate if CRC/VLAN 437 * should be stripped. 438 * 439 * If the unit allocator returns NO_ALLOCATOR_UNIT, then assume 440 * that a DMA allocator is not required, and use a regular memory 441 * allocator. 442 */ 443 444 int 445 bcm_pkt_alloc(int unit, int size, uint32 flags, bcm_pkt_t **pkt_buf) 446 { 447 bcm_pkt_t *pkt; 448 int rv = BCM_E_INTERNAL; 449 450 UNIT_VALID(unit); 451 452 pkt = sal_alloc(sizeof(bcm_pkt_t), "bcm_pkt_alloc"); 453 if (!pkt) { 454 *pkt_buf = NULL; 455 return BCM_E_MEMORY; 456 } 457 sal_memset(pkt, 0, sizeof(bcm_pkt_t)); 458 pkt->pkt_data = &pkt->_pkt_data; 459 pkt->blk_count = 1; 460 461 rv = _bcm_pkt_data_alloc(unit, size, pkt->pkt_data); 462 463 if (BCM_FAILURE(rv)) { 464 sal_free(pkt); 465 *pkt_buf = NULL; 466 return rv; 467 } 468 bcm_pkt_flags_init(unit, pkt, flags); 469 470 *pkt_buf = pkt; 471 472 return BCM_E_NONE; 473 } 474 475 476 /* 477 * Function: 478 * bcm_pkt_free 479 * Purpose: 480 * Basic, free routine using sal and soc functions 481 * Parameters: 482 * unit - Strata unit 483 * size - Bytes to allocate 484 * flags - See pkt.h for more about these flags 485 * Returns: 486 * Notes: 487 * Works with bcm_pkt_alloc. Checks for multiple blocks, 488 * so could be used with other routines that use sal_alloc 489 * and soc_cm_salloc. 490 */ 491 492 int 493 bcm_pkt_free(int unit, bcm_pkt_t *pkt) 494 { 495 int i; 496 soc_stat_t *stat = &SOC_CONTROL(unit)->stat; 497 498 UNIT_VALID(unit); 499 500 if (pkt) { 501 for (i = 0; i < pkt->blk_count; i++) { 502 if (pkt->pkt_data[i].data) { 503 _bcm_pkt_data_free(unit, &pkt->pkt_data[i]); 504 } 505 } 506 507 if (soc_feature(unit, soc_feature_cmicx)) { 508 if (pkt->tx_header) { 509 stat->dma_tbyt -= sizeof(pkt->_higig); 510 soc_cm_sfree(unit, pkt->tx_header); 511 pkt->tx_header = NULL; 512 } 513 } 514 515 sal_free(pkt); 516 } 517 518 return BCM_E_NONE; 519 } 520 521 /* 522 * Function: 523 * bcm_pkt_blk_alloc 524 * Purpose: 525 * Allocate an array of packets 526 * Parameters: 527 * unit - Strata device number 528 * count - How many packets to alloc 529 * size - Bytes in each packet 530 * flags - flags to use for packet alloc 531 * Returns: 532 * Pointer to allocated structure or NULL if fails 533 */ 534 535 int 536 bcm_pkt_blk_alloc(int unit, int count, int size, uint32 flags, 537 bcm_pkt_t ***packet_array) 538 { 539 bcm_pkt_t **p_array; 540 int i, j; 541 542 UNIT_VALID(unit); 543 544 if (!(p_array = sal_alloc(count * sizeof(bcm_pkt_t *), "pkt_blk"))) { 545 *packet_array = NULL; 546 return (BCM_E_MEMORY); 547 } 548 549 for (i = 0; i < count; i++) { 550 if (BCM_FAILURE(bcm_pkt_alloc(unit, size, flags, p_array + i))) { 551 552 for (j = 0; j < i; j++) { 553 bcm_pkt_free(unit, p_array[j]); 554 } 555 sal_free(p_array); 556 *packet_array = NULL; 557 return (BCM_E_MEMORY); 558 } 559 } 560 561 *packet_array = p_array; 562 return BCM_E_NONE; 563 } 564 565 566 /* 567 * Function: 568 * bcm_pkt_blk_free 569 * Purpose: 570 * Free an array of packets allocated with bcm_pkt_blk_alloc 571 * Parameters: 572 * unit - Strata device number 573 * pkts - pointer to array of packets 574 * count - How many packets to alloc 575 * Returns: 576 */ 577 578 int 579 bcm_pkt_blk_free(int unit, bcm_pkt_t **pkts, int count) 580 { 581 int i; 582 583 UNIT_VALID(unit); 584 585 if (pkts) { 586 for (i = 0; i < count; i++) { 587 if (pkts[i]) { 588 bcm_pkt_free(unit, pkts[i]); 589 } 590 } 591 sal_free(pkts); 592 } 593 594 return BCM_E_NONE; 595 } 596 597 598 /* 599 * Function: 600 * bcm_pkt_rx_alloc 601 * Purpose: 602 * Allocate a packet using RX alloc 603 * Parameters: 604 * len - length of data buffer 605 * Returns: 606 * Pointer to packet or NULL if memory error 607 * Notes: 608 * Sets up the packet to have a single buffer. 609 */ 610 611 int 612 bcm_pkt_rx_alloc(int unit, int len, bcm_pkt_t **pkt_buf) 613 { 614 bcm_pkt_t *pkt; 615 uint8 *buf; 616 int rv; 617 618 UNIT_VALID(unit); 619 620 buf = NULL; 621 pkt = sal_alloc(sizeof(bcm_pkt_t), "pkt_rx_alloc"); 622 if (pkt == NULL) { 623 *pkt_buf = NULL; 624 return BCM_E_MEMORY; 625 } 626 sal_memset(pkt, 0, sizeof(bcm_pkt_t)); 627 628 if (len > 0) { 629 rv = bcm_rx_alloc(unit, len, 0, (void*)&buf); 630 if (rv != BCM_E_NONE) { 631 sal_free(pkt); 632 return rv; 633 } 634 pkt->_pkt_data.data = buf; 635 pkt->_pkt_data.len = len; 636 pkt->pkt_len = len; 637 pkt->pkt_data = &pkt->_pkt_data; 638 pkt->blk_count = 1; 639 } 640 641 /* Set up flags here if possible */ 642 643 *pkt_buf = pkt; 644 return BCM_E_NONE; 645 } 646 647 /* 648 * Function: 649 * bcm_pkt_rx_free 650 * Purpose: 651 * Free a packet allocated using bcm_pkt_rx_alloc 652 * Parameters: 653 * pkt - packet to free 654 * Returns: 655 * Notes: 656 * Checks for null pkt and buffer 657 */ 658 659 int 660 bcm_pkt_rx_free(int unit, bcm_pkt_t *pkt) 661 { 662 UNIT_VALID(unit); 663 664 if (pkt) { 665 if (pkt->_pkt_data.data) { 666 bcm_rx_free(unit, pkt->_pkt_data.data); 667 } 668 sal_free(pkt); 669 } 670 671 return BCM_E_NONE; 672 } 673 674 /* 675 * Function: 676 * bcm_rx_reasons_t_init 677 * Purpose: 678 * Initialize RX reasons structure. 679 * Parameters: 680 * reasons - (INOUT) Pointer to the structure to be initialized. 681 * Returns: 682 * NONE 683 */ 684 void 685 bcm_rx_reasons_t_init(bcm_rx_reasons_t *reasons) 686 { 687 if (reasons != NULL) { 688 sal_memset(reasons, 0, sizeof (*reasons)); 689 } 690 return; 691 } 692 693 694 695 int 696 bcm_pkt_ecmp_grp_set(int unit, bcm_pkt_t *pkt, bcm_if_t ecmp_grp) 697 { 698 #if defined(INCLUDE_L3) && defined(BCM_FIREBOLT_SUPPORT) 699 if (ecmp_grp < BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit)) { 700 return BCM_E_PARAM; 701 } 702 703 pkt->txprocmh_ecmp_group_index = 704 ecmp_grp - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 705 706 return BCM_E_NONE; 707 #else 708 return BCM_E_CONFIG; 709 #endif 710 } 711 int 712 bcm_pkt_ecmp_member_set(int unit, bcm_pkt_t *pkt, bcm_if_t ecmp_grp, 713 bcm_if_t ecmp_member) 714 { 715 #if defined(INCLUDE_L3) && defined(BCM_FIREBOLT_SUPPORT) 716 int ecmp_group_idx = ecmp_grp - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 717 int ecmp_count = 0; 718 int *ecmp_mem, max_group_size, i; 719 int rv; 720 721 if (ecmp_grp < BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit)) { 722 return BCM_E_PARAM; 723 } 724 725 if (ecmp_member < BCM_XGS3_EGRESS_IDX_MIN(unit)) { 726 return BCM_E_PARAM; 727 } 728 729 ecmp_mem = sal_alloc(4096 * sizeof(bcm_if_t), "intf array"); 730 731 if (ecmp_mem == NULL) { 732 return BCM_E_MEMORY; 733 } 734 735 pkt->txprocmh_ecmp_group_index = 736 ecmp_grp - BCM_XGS3_MPATH_EGRESS_IDX_MIN(unit); 737 738 rv = _bcm_xgs3_ecmp_max_grp_size_get(unit, ecmp_group_idx, &max_group_size); 739 if (BCM_FAILURE(rv)) { 740 sal_free(ecmp_mem); 741 return rv; 742 } 743 744 rv = bcm_xgs3_l3_egress_multipath_get(unit, ecmp_grp, 745 max_group_size, ecmp_mem, &ecmp_count); 746 747 for( i = 0; i < ecmp_count; ++i) { 748 if (ecmp_mem[i] == ecmp_member) { 749 pkt->txprocmh_ecmp_member_index = i; 750 sal_free(ecmp_mem); 751 return BCM_E_NONE; 752 } 753 } 754 sal_free(ecmp_mem); 755 return BCM_E_PARAM; 756 #else 757 return BCM_E_CONFIG; 758 #endif 759 } 760 761 int 762 bcm_pkt_nexthop_set(int unit, bcm_pkt_t *pkt, bcm_if_t nexthop_id) 763 { 764 765 #if defined(INCLUDE_L3) && defined(BCM_FIREBOLT_SUPPORT) 766 if (nexthop_id < BCM_XGS3_EGRESS_IDX_MIN(unit)) { 767 return BCM_E_PARAM; 768 } 769 770 pkt->txprocmh_destination = nexthop_id - BCM_XGS3_EGRESS_IDX_MIN(unit); 771 return BCM_E_NONE; 772 #else 773 return BCM_E_CONFIG; 774 #endif 775 } 776