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 }