Shad Ansari | 2f7f9be | 2017-06-07 13:34:53 -0700 | [diff] [blame] | 1 | /****************************************************************************** |
| 2 | * |
| 3 | * <:copyright-BRCM:2016:DUAL/GPL:standard |
| 4 | * |
| 5 | * Copyright (c) 2016 Broadcom |
| 6 | * All Rights Reserved |
| 7 | * |
| 8 | * Unless you and Broadcom execute a separate written software license |
| 9 | * agreement governing use of this software, this software is licensed |
| 10 | * to you under the terms of the GNU General Public License version 2 |
| 11 | * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| 12 | * with the following added to such license: |
| 13 | * |
| 14 | * As a special exception, the copyright holders of this software give |
| 15 | * you permission to link this software with independent modules, and |
| 16 | * to copy and distribute the resulting executable under terms of your |
| 17 | * choice, provided that you also meet, for each linked independent |
| 18 | * module, the terms and conditions of the license of that module. |
| 19 | * An independent module is a module which is not derived from this |
| 20 | * software. The special exception does not apply to any modifications |
| 21 | * of the software. |
| 22 | * |
| 23 | * Not withstanding the above, under no circumstances may you combine |
| 24 | * this software in any way with any other Broadcom software provided |
| 25 | * under a license other than the GPL, without Broadcom's express prior |
| 26 | * written consent. |
| 27 | * |
| 28 | * :> |
| 29 | * |
| 30 | *****************************************************************************/ |
| 31 | |
| 32 | #ifndef TEST_SW_UTIL_LOOPBACK |
| 33 | |
| 34 | #include <bal_common.h> |
| 35 | #include <bcm_dev_log.h> |
| 36 | #include <bal_msg.h> |
| 37 | #include "bal_switch_util.h" |
| 38 | #include "bal_switch_acc_term.h" |
| 39 | #include "bal_dpp_acc_term.h" |
| 40 | #include "bal_dpp_qos.h" |
| 41 | #include "bal_dpp_qos_map.h" |
| 42 | |
| 43 | #include <bcm/types.h> |
| 44 | #include <bcm/port.h> |
| 45 | #include <bcm/rx.h> /* for dpp rpc rx register callback */ |
| 46 | #include <bcm/switch.h> |
| 47 | #include <bcm/l2.h> |
| 48 | |
| 49 | /** |
| 50 | * @file bal_dpp_acc_term.c |
| 51 | * @brief BAL Switch util functions that handle access terminal requests on DUNE PACKET PROCESSOR |
| 52 | * @addtogroup sw_util |
| 53 | * |
| 54 | */ |
| 55 | |
| 56 | /*@{*/ |
| 57 | |
| 58 | /* @brief L2 Table Operation Control |
| 59 | * |
| 60 | * This routine set the HW L2 learning and aging |
| 61 | * |
| 62 | * @param unit The device id |
| 63 | * @param flags Operation flags |
| 64 | * BCM_L2_LEARN_CPU 0x20 |
| 65 | * BCM_L2_INGRESS_DIST 0x02 |
| 66 | * BCM_L2_INGRESS_CENT 0x01 |
| 67 | * @param age_seconds L2 entry age out time in seconds |
| 68 | * @return BCM error code |
| 69 | */ |
| 70 | static int sw_util_dpp_l2_entry_control_set(int unit, int flags, int age_seconds) |
| 71 | { |
| 72 | int rv = 0; |
| 73 | |
| 74 | rv = bcm_switch_control_set(unit, bcmSwitchL2LearnMode, flags); |
| 75 | if (rv) |
| 76 | { |
| 77 | BCM_LOG(ERROR, log_id_sw_util, " bcm_switch_control_set failed ret = %d\n", rv); |
| 78 | return rv; |
| 79 | } |
| 80 | |
| 81 | /* set aging time */ |
| 82 | rv = bcm_l2_age_timer_set(unit, age_seconds); |
| 83 | if (rv) |
| 84 | { |
| 85 | BCM_LOG(ERROR, log_id_sw_util, " bcm_l2_age_timer_set failed ret = %d\n", rv); |
| 86 | return rv; |
| 87 | } |
| 88 | else |
| 89 | { |
| 90 | BCM_LOG(INFO, log_id_sw_util, " Set L2 table aging time to %d seconds\n", age_seconds); |
| 91 | } |
| 92 | |
| 93 | return rv; |
| 94 | |
| 95 | } |
| 96 | |
| 97 | |
| 98 | /** |
| 99 | * @brief L2 Table event handler |
| 100 | * |
| 101 | * This routine is a callback triggered by HW L2 Table events |
| 102 | * |
| 103 | * @param unit The device id |
| 104 | * @param p_l2addr Pointer to the L2 entry where the event is happen |
| 105 | * @param operation The type of event |
| 106 | * @param userdata Pointer to a user provided data when the handler is registered |
| 107 | */ |
| 108 | static void sw_util_dpp_l2_entry_event_handler(int unit, bcm_l2_addr_t *p_l2addr, int operation, void *userdata) |
| 109 | { |
| 110 | |
| 111 | if (p_l2addr == NULL) |
| 112 | { |
| 113 | BCM_LOG(ERROR, log_id_sw_util, " L2 entry callback with NULL L2 address, op = %d\n", operation); |
| 114 | return; |
| 115 | } |
| 116 | if (operation == BCM_L2_CALLBACK_LEARN_EVENT) |
| 117 | { |
| 118 | BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_LEARN_EVENT handler\n"); |
| 119 | } |
| 120 | else if (operation == BCM_L2_CALLBACK_MOVE_EVENT) |
| 121 | { |
| 122 | BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_MOVE_EVENT handler\n"); |
| 123 | } |
| 124 | else if (operation == BCM_L2_CALLBACK_AGE_EVENT) |
| 125 | { |
| 126 | BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_AGE_EVENT handler\n"); |
| 127 | } |
| 128 | else |
| 129 | { |
| 130 | BCM_LOG(DEBUG, log_id_sw_util, " BCM_L2_CALLBACK_OPERATION %d handler\n", operation); |
| 131 | } |
| 132 | |
| 133 | BCM_LOG(DEBUG, log_id_sw_util, " MAC %02x:%02x:%02x:%02x:%02x:%02x\n", |
| 134 | (0xff & p_l2addr->mac[0]), |
| 135 | (0xff & p_l2addr->mac[1]), |
| 136 | (0xff & p_l2addr->mac[2]), |
| 137 | (0xff & p_l2addr->mac[3]), |
| 138 | (0xff & p_l2addr->mac[4]), |
| 139 | (0xff & p_l2addr->mac[5]) ); |
| 140 | |
| 141 | if(!(p_l2addr->flags & BCM_L2_MCAST)) |
| 142 | { |
| 143 | BCM_LOG(DEBUG, log_id_sw_util, " UC VID=0x%x| PORT=0x%08x\n", p_l2addr->vid, p_l2addr->port); |
| 144 | /* print_gport_part - p_l2addr->port */ |
| 145 | { |
| 146 | int a = -1, b = 0; |
| 147 | char* type = ""; |
| 148 | if (a==-1){ |
| 149 | a=BCM_GPORT_LOCAL_GET(p_l2addr->port); |
| 150 | type ="local"; |
| 151 | } |
| 152 | if (a==-1){ |
| 153 | a=BCM_GPORT_MODPORT_MODID_GET(p_l2addr->port); |
| 154 | b=BCM_GPORT_MODPORT_PORT_GET(p_l2addr->port); |
| 155 | type ="modport"; |
| 156 | } |
| 157 | if (a==-1){ |
| 158 | a=BCM_GPORT_TRUNK_GET(p_l2addr->port); |
| 159 | type ="trunk"; |
| 160 | } |
| 161 | if (a==-1){ |
| 162 | a=BCM_GPORT_MCAST_GET(p_l2addr->port); |
| 163 | type ="mact"; |
| 164 | } |
| 165 | if (a==-1){ |
| 166 | a=BCM_GPORT_MPLS_PORT_ID_GET(p_l2addr->port); |
| 167 | type ="mpls_port"; |
| 168 | } |
| 169 | if (a==-1){ |
| 170 | a=BCM_GPORT_VLAN_PORT_ID_GET(p_l2addr->port); |
| 171 | type ="vlan_port"; |
| 172 | } |
| 173 | if (a==-1){ |
| 174 | a=BCM_GPORT_SYSTEM_PORT_ID_GET(p_l2addr->port); |
| 175 | type ="sys_port"; |
| 176 | } |
| 177 | if (a==-1){ |
| 178 | a=BCM_GPORT_MIRROR_GET(p_l2addr->port); |
| 179 | } |
| 180 | BCM_LOG(DEBUG, log_id_sw_util, " GPORT %s <0x%x,%d>\n", type, a, b); |
| 181 | } |
| 182 | } |
| 183 | else |
| 184 | { |
| 185 | BCM_LOG(DEBUG, log_id_sw_util, " MC 0x%08x\n",p_l2addr->l2mc_group); |
| 186 | } |
| 187 | BCM_LOG(DEBUG, log_id_sw_util, " static %d|\n", (p_l2addr->flags & BCM_L2_STATIC)!=0 ); |
| 188 | |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * @brief Connect access terminal with DPP as part of the components |
| 193 | * |
| 194 | * This routine is called by sw_util_access_terminal_connect in the BAL core |
| 195 | * to execute DPP specific API for access_terminal_connect request |
| 196 | * |
| 197 | * @param p_net_map Pointer to the net ports mapping from logical numbrer to physical number |
| 198 | * @param p_pon_map Pointer to the pon ports mapping from logical numbrer to physical number |
| 199 | * @return bcmos_errno |
| 200 | */ |
| 201 | bcmos_errno sw_util_dpp_acc_term_connect(bal_swapp_port *p_net_map, bal_swapp_port *p_pon_map ) |
| 202 | { |
| 203 | bcmos_errno ret = BCM_ERR_OK; |
| 204 | int rc = 0; |
| 205 | bal_swapp_port *port; |
| 206 | |
| 207 | BCM_LOG(INFO, log_id_sw_util, " DPP - Got a access terminal CONNECT\n"); |
| 208 | |
| 209 | do |
| 210 | { |
| 211 | /* setup the device ID - This is very hardware specific */ |
| 212 | port = p_net_map; |
| 213 | /* -1 indicate the end of table */ |
| 214 | while(port->pbm_id != -1) |
| 215 | { |
| 216 | |
| 217 | /* the default TPID is 0x8100, add 0x88a8 to the allow TPID */ |
| 218 | |
| 219 | rc = bcm_port_tpid_delete_all(port->device_id, port->pbm_id); |
| 220 | if (rc) |
| 221 | { |
| 222 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to clear the nni TPID list on interface %d\n", port->pbm_id); |
| 223 | ret = BCM_ERR_INTERNAL; |
| 224 | } |
| 225 | rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x8100, 0); |
| 226 | if (rc) |
| 227 | { |
| 228 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x8100 to the nni TPID list on interface %d\n", port->pbm_id); |
| 229 | ret = BCM_ERR_INTERNAL; |
| 230 | } |
| 231 | rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x88a8, 0); |
| 232 | if (rc) |
| 233 | { |
| 234 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x88a8 to the nni TPID list on interface %d\n", port->pbm_id); |
| 235 | ret = BCM_ERR_INTERNAL; |
| 236 | } |
| 237 | |
| 238 | port++; |
| 239 | } |
| 240 | |
| 241 | port = p_pon_map; |
| 242 | while(port->pbm_id != -1) |
| 243 | { |
| 244 | |
| 245 | /* the default TPID is 0x8100, add 0x88a8 to the allow TPID */ |
| 246 | |
| 247 | rc = bcm_port_tpid_delete_all(port->device_id, port->pbm_id); |
| 248 | if (rc) |
| 249 | { |
| 250 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to clear the pon TPID list on interface %d\n", port->pbm_id); |
| 251 | ret = BCM_ERR_INTERNAL; |
| 252 | } |
| 253 | rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x8100, 0); |
| 254 | if (rc) |
| 255 | { |
| 256 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x8100 to the pon TPID list on interface %d\n", port->pbm_id); |
| 257 | ret = BCM_ERR_INTERNAL; |
| 258 | } |
| 259 | rc = bcm_port_tpid_add(port->device_id, port->pbm_id, 0x88a8, 0); |
| 260 | if (rc) |
| 261 | { |
| 262 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to add 0x88a8 to the pon TPID list on interface %d\n", port->pbm_id); |
| 263 | ret = BCM_ERR_INTERNAL; |
| 264 | } |
| 265 | |
| 266 | port++; |
| 267 | } |
| 268 | |
| 269 | if(ret != BCM_ERR_OK) |
| 270 | { |
| 271 | /* exit if port init failed */ |
| 272 | break; |
| 273 | } |
| 274 | |
| 275 | /* Remove all ports from VLAN 1, so the L2 broadcast won't send to CPU port by default */ |
| 276 | bcm_vlan_gport_delete_all(bal_bcm_dft_dev_get(), 1); |
| 277 | |
| 278 | /* configure all qos map tables */ |
| 279 | ret = bal_sw_dpp_pcp_remark_maps_init(bal_bcm_dft_dev_get()); |
| 280 | if (ret) |
| 281 | { |
| 282 | BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to init qos map tables\n"); |
| 283 | break; |
| 284 | } |
| 285 | |
| 286 | /* init the DS QOS hierarchical scheduling - ignore error if HW dos not support QOS */ |
| 287 | ret =bal_sw_dpp_qos_init(bal_bcm_dft_dev_get(), bal_bcm_intf_maptable_get()); |
| 288 | if (ret) |
| 289 | { |
| 290 | BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to init qos HR scheduler\n"); |
| 291 | break; |
| 292 | } |
| 293 | |
| 294 | rc = bcm_l2_addr_register(bal_bcm_dft_dev_get(), sw_util_dpp_l2_entry_event_handler, NULL); |
| 295 | if (rc) |
| 296 | { |
| 297 | BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to register l2_addr callback \n"); |
| 298 | ret = BCM_ERR_INTERNAL; |
| 299 | break; |
| 300 | } |
| 301 | |
| 302 | rc = sw_util_dpp_l2_entry_control_set(bal_bcm_dft_dev_get(), BCM_L2_LEARN_CPU|BCM_L2_INGRESS_DIST, bal_bcm_l2_age_time_get()); |
| 303 | if (rc) |
| 304 | { |
| 305 | BCM_LOG(WARNING, log_id_sw_util, " DPP - fail to set l2_addr control \n"); |
| 306 | ret = BCM_ERR_INTERNAL; |
| 307 | break; |
| 308 | } |
| 309 | }while(0); |
| 310 | return ret; |
| 311 | } |
| 312 | |
| 313 | /* internal structure used by the trap receiving thread */ |
| 314 | typedef struct |
| 315 | { |
| 316 | int udp_port; |
| 317 | pthread_t threadid; |
| 318 | dpp_rx_cb_f callback; |
| 319 | } trap_context; |
| 320 | |
| 321 | static trap_context g_trap_ctx = {0}; |
| 322 | |
| 323 | /* the adjustment needed for ING trap packet header */ |
| 324 | #define DEFAULT_SOP_ADJ 2 |
| 325 | #define DEFAULT_REASON_ADJ 4 |
| 326 | /* compiler restirct the total local variables size < 16384 */ |
| 327 | #define DEFAULT_TRAP_BUF_SIZE (10 *1024) |
| 328 | /* |
| 329 | the listener thread that wait for the trap packet_in message from the switch |
| 330 | */ |
| 331 | static void *trap_receive(void *p_user_data) |
| 332 | { |
| 333 | int rc; |
| 334 | int sUDPSocket; |
| 335 | unsigned char cBuffer[DEFAULT_TRAP_BUF_SIZE]; |
| 336 | int nBytesRecv = 0; |
| 337 | int nBufSize = DEFAULT_TRAP_BUF_SIZE; |
| 338 | socklen_t nReceiveAddrSize = 0; |
| 339 | int maxfd; |
| 340 | fd_set read_fds; |
| 341 | struct timeval tv; |
| 342 | uint16_t tmp_src_port, src_port; |
| 343 | trap_context *p_trap_ctx = (trap_context *)p_user_data; |
| 344 | uint32_t tmp_reason, reason; |
| 345 | /* Create a connectionless socket */ |
| 346 | sUDPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| 347 | /* Check to see if we have a valid socket */ |
| 348 | if(sUDPSocket < 0) |
| 349 | { |
| 350 | BCM_LOG(ERROR, log_id_sw_util, " DPP - trap_receive:create socket failed\n"); |
| 351 | return NULL; |
| 352 | } |
| 353 | |
| 354 | // Setup a bind on the socket, telling us what port and |
| 355 | // adapter to receive datagrams on. |
| 356 | struct sockaddr_in sReceiveFromAddr; |
| 357 | memset(&sReceiveFromAddr, 0, sizeof(struct sockaddr_in)); |
| 358 | |
| 359 | sReceiveFromAddr.sin_family = AF_INET; |
| 360 | sReceiveFromAddr.sin_port = htons(p_trap_ctx->udp_port); |
| 361 | sReceiveFromAddr.sin_addr.s_addr = htonl(INADDR_ANY); |
| 362 | |
| 363 | rc = bind(sUDPSocket, (struct sockaddr *)&sReceiveFromAddr, |
| 364 | sizeof(struct sockaddr_in)); |
| 365 | if (rc < 0) |
| 366 | { |
| 367 | BCM_LOG(ERROR, log_id_sw_util, " DPP - trap_receive:bind failed\n"); |
| 368 | return NULL; |
| 369 | } |
| 370 | |
| 371 | BCM_LOG(INFO, log_id_sw_util, " DPP - trap_receive start listen at %d\n", p_trap_ctx->udp_port); |
| 372 | |
| 373 | // Receive a datagram from another device |
| 374 | while(1) |
| 375 | { |
| 376 | FD_ZERO(&read_fds); |
| 377 | FD_SET(sUDPSocket, &read_fds); |
| 378 | maxfd = sUDPSocket; |
| 379 | /* Set the timeout */ |
| 380 | tv.tv_sec = 3; |
| 381 | tv.tv_usec = 0; /* 3 seconds */ |
| 382 | rc = select(maxfd + 1, &read_fds, NULL, NULL, &tv); |
| 383 | |
| 384 | if (rc < 0) |
| 385 | { |
| 386 | BCM_LOG(ERROR, log_id_sw_util, " DPP - trap_receive:select failed err = %d\n", rc); |
| 387 | break; |
| 388 | } |
| 389 | if (rc == 0) /* timeout */ |
| 390 | { |
| 391 | continue; |
| 392 | } |
| 393 | // Get the datagrama |
| 394 | nBytesRecv = recvfrom(sUDPSocket, cBuffer, nBufSize, 0, |
| 395 | (struct sockaddr *) &sReceiveFromAddr, |
| 396 | &nReceiveAddrSize); |
| 397 | BCM_LOG(INFO, log_id_sw_util, " DPP - Got %d bytes message \n", nBytesRecv); |
| 398 | |
| 399 | /* the first 4 bytes are reason */ |
| 400 | /* compiler generate cast-align warning -> p_reason = (uint32_t *)(cBuffer); */ |
| 401 | memcpy(&tmp_reason, cBuffer, sizeof(uint32_t) ); |
| 402 | reason = ntohl(tmp_reason); |
| 403 | /* the next 2 bytes are srouce port */ |
| 404 | /* compiler generate cast-align warning -> p_src_port = (uint16 *)(cBuffer + DEFAULT_REASON_ADJ); */ |
| 405 | memcpy(&tmp_src_port, cBuffer + DEFAULT_REASON_ADJ, sizeof(uint16_t)); |
| 406 | src_port = ntohs(tmp_src_port); |
| 407 | |
| 408 | /* call the register callback here - set unit to 0 as it is don't care */ |
| 409 | if(p_trap_ctx->callback) |
| 410 | { |
| 411 | p_trap_ctx->callback(0, src_port, reason, cBuffer+DEFAULT_SOP_ADJ+DEFAULT_REASON_ADJ, nBytesRecv-DEFAULT_SOP_ADJ-DEFAULT_REASON_ADJ); |
| 412 | } |
| 413 | } |
| 414 | close(sUDPSocket); |
| 415 | |
| 416 | return NULL; |
| 417 | } |
| 418 | |
| 419 | /** |
| 420 | * @brief Start a receiving thread and add the callbck to process CPU trapped packets |
| 421 | * |
| 422 | * This routine is called by sw_util_access_terminal_connect in the BAL core |
| 423 | * to execute DPP specific API for process the trapping packets |
| 424 | * |
| 425 | * @param unit the switch device number the callback applied |
| 426 | * @param cb_f a callback function that process the trapped packets |
| 427 | * @return bcmos_errno |
| 428 | */ |
| 429 | bcmos_errno sw_util_dpp_rx_cb_register(uint32_t unit, dpp_rx_cb_f cb_f) |
| 430 | { |
| 431 | int ret; |
| 432 | |
| 433 | /* if the receiving thread already started, return error */ |
| 434 | if (g_trap_ctx.threadid) |
| 435 | { |
| 436 | BCM_LOG(ERROR, log_id_sw_util, " DPP - There is an existing trap receiving thread\n"); |
| 437 | return BCM_ERR_INTERNAL; |
| 438 | } |
| 439 | |
| 440 | g_trap_ctx.udp_port = bal_bcm_trap_rcv_port_get(); |
| 441 | g_trap_ctx.callback = cb_f; |
| 442 | |
| 443 | /* use default attribute to create the thread */ |
| 444 | ret = pthread_create(&g_trap_ctx.threadid, NULL, &trap_receive, &g_trap_ctx); |
| 445 | |
| 446 | if(ret) |
| 447 | { |
| 448 | BCM_LOG(ERROR, log_id_sw_util, " DPP - fail to create trap receiving thread - %d\n", ret); |
| 449 | return BCM_ERR_INTERNAL; |
| 450 | } |
| 451 | |
| 452 | return BCM_ERR_OK; |
| 453 | } |
| 454 | |
| 455 | /* To make compiler happy when build with ESW switch only */ |
| 456 | #ifdef ESW_SWITCH |
| 457 | void dpp_dft_rx_cb(int unit, int dst_port, int reason, unsigned char *payload, int payload_len) |
| 458 | { |
| 459 | return; |
| 460 | } |
| 461 | #else |
| 462 | extern void dpp_dft_rx_cb(int unit, int dst_port, int reason, unsigned char *payload, int payload_len); |
| 463 | #endif |
| 464 | |
| 465 | #define L2_HEADER_SIZE (12) /* bytes */ |
| 466 | |
| 467 | #define TUNNEL_TAG_TPID_LEN (2) /* bytes - TPID (2 bytes) */ |
| 468 | #define TUNNEL_TAG_PBITS_CFI_VLAN_ID_LEN (2) /* PBITS+CFI+VLAN ID (2 bytes) */ |
| 469 | #define TUNNEL_TAG_SIZE (TUNNEL_TAG_TPID_LEN + TUNNEL_TAG_PBITS_CFI_VLAN_ID_LEN) |
| 470 | |
| 471 | bcmos_errno sw_util_dpp_pkt_send(int target_port_id, |
| 472 | int reason, |
| 473 | unsigned char *p_user_pkt, |
| 474 | int user_pkt_len, |
| 475 | trap_target target_device, |
| 476 | int target_tunnel_id) |
| 477 | { |
| 478 | bcmos_errno ret = BCM_ERR_OK; |
| 479 | char *p_pktout_msg; |
| 480 | uint8_t dst_port_id; |
| 481 | int dst_device_id; |
| 482 | char *p_pktout_ptr; |
| 483 | uint32_t total_msg_size; |
| 484 | uint32_t uint32_num; |
| 485 | uint16_t uint16_num; |
| 486 | |
| 487 | /* |
| 488 | * Allocate a message to be sent to the bcm.user instance. This will contain |
| 489 | * the packet to be sent out of the switch, as well has header metadata. |
| 490 | */ |
| 491 | p_pktout_msg = bcmos_calloc(user_pkt_len + |
| 492 | DEFAULT_SOP_ADJ + |
| 493 | DEFAULT_REASON_ADJ + |
| 494 | TUNNEL_TAG_SIZE); |
| 495 | |
| 496 | if(NULL == p_pktout_msg) |
| 497 | { |
| 498 | return BCM_ERR_NOMEM; |
| 499 | } |
| 500 | |
| 501 | /* translate the output port to the bcm.user's mapping */ |
| 502 | switch(reason) |
| 503 | { |
| 504 | case REASON_SEND_TO_NNI: |
| 505 | dst_port_id = bal_bcm_net_inf_pbm_get(target_port_id); |
| 506 | dst_device_id = bal_bcm_net_inf_dev_get(target_port_id); |
| 507 | break; |
| 508 | case REASON_SEND_TO_PON: |
| 509 | dst_port_id = bal_bcm_pon_inf_pbm_get(target_port_id); |
| 510 | dst_device_id = bal_bcm_pon_inf_dev_get(target_port_id); |
| 511 | break; |
| 512 | default: |
| 513 | return BCM_ERR_PARM; |
| 514 | break; |
| 515 | } |
| 516 | |
| 517 | /* Format of the message to the bcm.user instance is: |
| 518 | * |
| 519 | * reason code: 4 bytes |
| 520 | * dst_port: 2 bytes |
| 521 | * user packet: pkt_size bytes |
| 522 | */ |
| 523 | |
| 524 | /* Insert the 4 bytes with reason code */ |
| 525 | uint32_num = (htonl(reason)); |
| 526 | memcpy(p_pktout_msg, &uint32_num, sizeof(uint32_num)); |
| 527 | |
| 528 | /* Replace 2 bytes with packet_out destination port info. This is the bcm.user's |
| 529 | * mapping of output port (NNI or PON) to BCM SDK port mapping */ |
| 530 | uint16_num = htons(dst_port_id & 0xff); /* dst_port contents is actually only 1 byte */ |
| 531 | memcpy(&p_pktout_msg[DEFAULT_REASON_ADJ], &uint16_num, sizeof(uint16_num)); |
| 532 | |
| 533 | /* Copy in the user packet to send to the bcm.user for transmission out of the switch. |
| 534 | * Remember to insert the proper tunnel tag in the message for out_ports that are |
| 535 | * associated with the PON |
| 536 | */ |
| 537 | p_pktout_ptr = p_pktout_msg + DEFAULT_REASON_ADJ + DEFAULT_SOP_ADJ; |
| 538 | total_msg_size = user_pkt_len + DEFAULT_REASON_ADJ + DEFAULT_SOP_ADJ; |
| 539 | |
| 540 | if(reason == REASON_SEND_TO_NNI) |
| 541 | { |
| 542 | /* Copy in the entire packet as-is */ |
| 543 | memcpy(p_pktout_ptr, p_user_pkt, user_pkt_len); |
| 544 | } |
| 545 | else |
| 546 | { |
| 547 | uint16_t pktout_offset = 0; |
| 548 | |
| 549 | /* Copy in the L2 header (MAC DA/SA) */ |
| 550 | memcpy(&p_pktout_ptr[pktout_offset], p_user_pkt, L2_HEADER_SIZE); |
| 551 | |
| 552 | /* Point to the tunnel tag area in the outgoing message */ |
| 553 | pktout_offset += L2_HEADER_SIZE; |
| 554 | |
| 555 | /* Insert the TPID in the tag for the output destination */ |
| 556 | uint16_num = htons(0x8100); |
| 557 | memcpy(&p_pktout_ptr[pktout_offset], &uint16_num, sizeof(uint16_num)); |
| 558 | |
| 559 | /* Point to the remainder of the outgoing message */ |
| 560 | pktout_offset += TUNNEL_TAG_TPID_LEN; |
| 561 | /* Insert the tunnel tag vlan ID for the output destination */ |
| 562 | uint16_num = htons(target_tunnel_id); |
| 563 | memcpy(&p_pktout_ptr[pktout_offset], &uint16_num, sizeof(uint16_num)); |
| 564 | |
| 565 | /* Point to the remainder of the outgoing message */ |
| 566 | pktout_offset += TUNNEL_TAG_PBITS_CFI_VLAN_ID_LEN; |
| 567 | |
| 568 | /* Copy in the rest of the message */ |
| 569 | memcpy(&p_pktout_ptr[pktout_offset], |
| 570 | (p_user_pkt + L2_HEADER_SIZE), |
| 571 | (user_pkt_len - L2_HEADER_SIZE)); |
| 572 | |
| 573 | total_msg_size += TUNNEL_TAG_SIZE; |
| 574 | } |
| 575 | |
| 576 | |
| 577 | BCM_LOG(DEBUG, log_id_sw_util, "Packet send (user pkt_size:%d) destined for %s port %d\n", |
| 578 | user_pkt_len, |
| 579 | (reason == REASON_SEND_TO_PON) ? "PON" : "NNI", |
| 580 | dst_port_id); |
| 581 | |
| 582 | if(reason == REASON_SEND_TO_PON) |
| 583 | { |
| 584 | BCM_LOG(DEBUG, log_id_sw_util, "Sending via GEM %d\n", target_tunnel_id); |
| 585 | } |
| 586 | |
| 587 | if (bal_bcm_use_rpc_get()) |
| 588 | { |
| 589 | /* On systems where BAL runs remotely from the switch hardware, we |
| 590 | * send the packet out message to the remote bcm.user process. |
| 591 | * That process then sends the attached user packet to the specified switch port. |
| 592 | */ |
| 593 | |
| 594 | sendto(target_device.socket, |
| 595 | p_pktout_msg, |
| 596 | total_msg_size, 0, |
| 597 | (struct sockaddr *) &(target_device.addr), |
| 598 | sizeof(struct sockaddr_in)); |
| 599 | |
| 600 | } |
| 601 | else |
| 602 | { |
| 603 | /* On systems where BAL runs on the CPU co-located with the switch hardware, |
| 604 | * we send the attached user packet to the specified switch port directly. |
| 605 | */ |
| 606 | |
| 607 | total_msg_size = total_msg_size - ( DEFAULT_REASON_ADJ + DEFAULT_SOP_ADJ ); |
| 608 | dpp_dft_tx_cb(dst_device_id, |
| 609 | dst_port_id, |
| 610 | reason, |
| 611 | (unsigned char *)p_pktout_ptr, |
| 612 | total_msg_size); |
| 613 | } |
| 614 | |
| 615 | bcmos_free(p_pktout_msg); |
| 616 | |
| 617 | BCM_LOG(INFO, log_id_sw_util, "CPU packet msg sent to bcm.user for packet with msg size of %d (payload size: %d)\n", |
| 618 | total_msg_size, user_pkt_len); |
| 619 | |
| 620 | return ret; |
| 621 | } |
| 622 | |
| 623 | /*@}*/ |
| 624 | #endif /* #ifndef TEST_SW_UTIL_LOOPBACK */ |