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 | /** |
| 33 | * @file bal_api_worker.c |
| 34 | * @brief Main processing loop for the worker thread that handles INDications |
| 35 | * sent from the core to the BAL public API |
| 36 | * |
| 37 | */ |
| 38 | |
| 39 | /* |
| 40 | * We need access to the BAL subsystem names |
| 41 | */ |
| 42 | #define BAL_SUBSYSTEM_STR_REQ |
| 43 | |
| 44 | #include <bcmos_system.h> |
| 45 | #include <bal_msg.h> |
| 46 | #include <bal_obj_msg_pack_unpack.h> |
| 47 | #include <bal_osmsg.h> |
| 48 | #include <bal_api.h> |
| 49 | #include "bal_api_worker.h" |
| 50 | #ifdef BAL_MONOLITHIC |
| 51 | #include <bal_worker.h> |
| 52 | #endif |
| 53 | #ifdef ENABLE_LOG |
| 54 | #include <bcm_dev_log.h> |
| 55 | #endif |
| 56 | |
| 57 | /* This rx thread and worker thread are used to process indications from the core |
| 58 | */ |
| 59 | static bcmos_task api_ind_rx_thread; |
| 60 | static bcmos_task api_ind_worker_thread; |
| 61 | |
| 62 | /* Local function declarations */ |
| 63 | static int _bal_ipc_api_ind_rx_handler(long data); |
| 64 | static void bal_ipc_api_indication_handler(bcmos_module_id module_id, bcmos_msg *msg); |
| 65 | |
| 66 | |
| 67 | typedef struct indication_subscription_inst indication_subscription_inst; |
| 68 | struct indication_subscription_inst |
| 69 | { |
| 70 | bcmbal_cb_cfg cb_cfg; |
| 71 | /**< TAILQ link for list management */ |
| 72 | TAILQ_ENTRY(indication_subscription_inst) indication_subscription_inst_next; |
| 73 | }; |
| 74 | |
| 75 | TAILQ_HEAD(indication_subscription_list_head, indication_subscription_inst) indication_subscription_list; |
| 76 | |
| 77 | /* |
| 78 | * The queue through which the core converses with |
| 79 | * the backend of the Public API for indications. |
| 80 | */ |
| 81 | static bcmos_msg_queue bal_api_ind_backend_queue; |
| 82 | bcmos_msg_queue *p_bal_api_ind_queue; |
| 83 | |
| 84 | /* Create API backend message queue */ |
| 85 | bcmos_errno bal_api_ind_msg_queue_create(mgmt_queue_addr_ports *mgmt_queue_info) |
| 86 | { |
| 87 | bcmos_msg_queue_parm msg_q_p = {}; |
| 88 | bcmos_errno ret = BCM_ERR_OK; |
| 89 | |
| 90 | do |
| 91 | { |
| 92 | /* Create BAL API indication receive queue - the core sends IND messages |
| 93 | * to the BAL public API using this queue. |
| 94 | */ |
| 95 | msg_q_p.name = "api_ind_rx_q"; |
| 96 | |
| 97 | if (NULL != mgmt_queue_info->balapi_mgmt_ip_port) |
| 98 | { |
| 99 | uint16_t portnum; |
| 100 | char *p_ind_portnum_str; |
| 101 | char balapi_ind_port_str[256]; |
| 102 | |
| 103 | /* |
| 104 | * make a copy of the user chosen bal api mgmt port |
| 105 | */ |
| 106 | strcpy(balapi_ind_port_str, mgmt_queue_info->balapi_mgmt_ip_port); |
| 107 | |
| 108 | /* Find the port number */ |
| 109 | p_ind_portnum_str = strchr(balapi_ind_port_str, ':') + 1; |
| 110 | |
| 111 | /* convert to an integer and increment it by one */ |
| 112 | portnum = atoi(p_ind_portnum_str) + 1; |
| 113 | |
| 114 | /* create the new string defining the BAL API indication port */ |
| 115 | sprintf(p_ind_portnum_str,"%d", portnum); |
| 116 | |
| 117 | /* Set up the BAL API indication IP:port access parameter |
| 118 | */ |
| 119 | msg_q_p.local_ep_address = balapi_ind_port_str; |
| 120 | msg_q_p.remote_ep_address = NULL; |
| 121 | msg_q_p.ep_type = BCMOS_MSG_QUEUE_EP_UDP_SOCKET; |
| 122 | } |
| 123 | else |
| 124 | { |
| 125 | msg_q_p.ep_type = BCMOS_MSG_QUEUE_EP_LOCAL; |
| 126 | } |
| 127 | |
| 128 | ret = bcmos_msg_queue_create(&bal_api_ind_backend_queue, &msg_q_p); |
| 129 | if (BCM_ERR_OK != ret) |
| 130 | { |
| 131 | BCM_LOG(ERROR, log_id_public_api, "Couldn't BAL API rx indication queue\n"); |
| 132 | break; |
| 133 | } |
| 134 | |
| 135 | p_bal_api_ind_queue = &bal_api_ind_backend_queue; |
| 136 | #ifdef BAL_MONOLITHIC |
| 137 | if (NULL == mgmt_queue_info->balapi_mgmt_ip_port) |
| 138 | p_bal_core_to_api_ind_queue = p_bal_api_ind_queue; |
| 139 | #endif |
| 140 | } while (0); |
| 141 | |
| 142 | return ret; |
| 143 | } |
| 144 | |
| 145 | /* Worker module init function. |
| 146 | * Register for messages this module is expected to receive |
| 147 | */ |
| 148 | static bcmos_errno _bal_worker_module_bal_api_init(long data) |
| 149 | { |
| 150 | bcmos_task_parm task_p = {}; |
| 151 | bcmos_errno ret = BCM_ERR_OK; |
| 152 | |
| 153 | BUG_ON(0 == data); |
| 154 | |
| 155 | do |
| 156 | { |
| 157 | /* Create BAL API indication RX thread */ |
| 158 | task_p.name = "ipc_api_ind_rx_thread"; |
| 159 | task_p.priority = TASK_PRIORITY_IPC_RX; |
| 160 | task_p.handler = _bal_ipc_api_ind_rx_handler; |
| 161 | task_p.data = (long)&bal_api_ind_backend_queue; |
| 162 | |
| 163 | ret = bcmos_task_create(&api_ind_rx_thread, &task_p); |
| 164 | if (ret) |
| 165 | { |
| 166 | BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API indication RX thread\n"); |
| 167 | break; |
| 168 | } |
| 169 | |
| 170 | /* Register the message types to be handled by the mgmt module |
| 171 | */ |
| 172 | bcmos_msg_register(BCMBAL_MGMT_API_IND_MSG, |
| 173 | 0, |
| 174 | BCMOS_MODULE_ID_WORKER_API_IND, bal_ipc_api_indication_handler); |
| 175 | |
| 176 | } |
| 177 | while(0); |
| 178 | |
| 179 | return ret; |
| 180 | } |
| 181 | |
| 182 | static int _bal_ipc_api_ind_rx_handler(long data) |
| 183 | { |
| 184 | bcmos_msg_queue *rxq = (bcmos_msg_queue *)data; |
| 185 | bcmos_task *my_task = bcmos_task_current(); |
| 186 | bcmos_msg *msg; |
| 187 | bcmos_errno ret = BCM_ERR_OK; |
| 188 | void *payload; |
| 189 | |
| 190 | while (!my_task->destroy_request) |
| 191 | { |
| 192 | payload = NULL; |
| 193 | ret = bcmbal_msg_recv(rxq, BCMOS_WAIT_FOREVER, &payload); |
| 194 | if (ret) |
| 195 | { |
| 196 | /* Unexpected failure */ |
| 197 | BCM_LOG(ERROR, log_id_public_api, "bcmbal_msg_recv() -> %s\n", bcmos_strerror(ret)); |
| 198 | continue; |
| 199 | } |
| 200 | |
| 201 | /* Message received */ |
| 202 | BCM_LOG(DEBUG, log_id_public_api, "bcmbal_msg_recv(%p) -> %s\n", payload, bcmos_strerror(ret)); |
| 203 | |
| 204 | /* |
| 205 | * Got a message, so now dispatch it. This will result in one |
| 206 | * of the modules (registered for the message being processed) |
| 207 | * executing its message callback handler. |
| 208 | */ |
| 209 | msg = bcmbal_bcmos_hdr_get(payload); |
| 210 | ret = bcmos_msg_dispatch(msg, BCMOS_MSG_SEND_AUTO_FREE); |
| 211 | if (ret) |
| 212 | { |
| 213 | BCM_LOG(ERROR, log_id_public_api, |
| 214 | "Couldn't dispatch message %d:%d\n", |
| 215 | (int)msg->type, (int)msg->instance); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | my_task->destroyed = BCMOS_TRUE; |
| 220 | |
| 221 | return (BCM_ERR_OK == ret) ? 0 : -EINVAL; |
| 222 | } |
| 223 | |
| 224 | |
| 225 | /* Message wrapper that is called when indication is delivered in application module's context */ |
| 226 | static void _int_deliver_wrapper_cb(bcmos_module_id module_id, bcmos_msg *msg) |
| 227 | { |
| 228 | void *msg_payload = bcmbal_payload_ptr_get(bcmbal_bal_hdr_get_by_bcmos_hdr(msg)); |
| 229 | f_bcmbal_ind_handler handler = (f_bcmbal_ind_handler)bcmbal_scratchpad_get(msg_payload); |
| 230 | |
| 231 | handler((bcmbal_obj *)msg_payload); |
| 232 | bcmbal_msg_free((bcmbal_obj *)msg_payload); |
| 233 | } |
| 234 | |
| 235 | static void process_listener_callbacks(bcmbal_obj *obj) |
| 236 | { |
| 237 | |
| 238 | indication_subscription_inst *current_entry, *p_temp_entry; |
| 239 | |
| 240 | BCM_LOG(DEBUG, log_id_public_api, "inspecting registered callback for object %d\n", |
| 241 | obj->obj_type); |
| 242 | |
| 243 | TAILQ_FOREACH_SAFE(current_entry, |
| 244 | &indication_subscription_list, |
| 245 | indication_subscription_inst_next, |
| 246 | p_temp_entry) |
| 247 | { |
| 248 | BCM_LOG(DEBUG, log_id_public_api, "entry objtype %d\n", current_entry->cb_cfg.obj_type); |
| 249 | |
| 250 | if((BCMBAL_OBJ_ID_ANY == current_entry->cb_cfg.obj_type) || |
| 251 | (current_entry->cb_cfg.obj_type == obj->obj_type)) |
| 252 | { |
| 253 | BCM_LOG(DEBUG, log_id_public_api, |
| 254 | "Calling registered callback for object %d\n", |
| 255 | obj->obj_type); |
| 256 | |
| 257 | /* call the registered function directly or in the target module's context */ |
| 258 | if (BCMOS_MODULE_ID_NONE == current_entry->cb_cfg.module) |
| 259 | { |
| 260 | current_entry->cb_cfg.ind_cb_hdlr(obj); |
| 261 | } |
| 262 | else |
| 263 | { |
| 264 | bcmbal_obj *clone = bcmbal_msg_clone(obj); |
| 265 | bcmos_errno err; |
| 266 | if (NULL == clone) |
| 267 | { |
| 268 | BCM_LOG(ERROR, log_id_public_api, |
| 269 | "Couldn't clone message for object %d\n", |
| 270 | obj->obj_type); |
| 271 | continue; |
| 272 | } |
| 273 | bcmbal_scratchpad_set(clone, current_entry->cb_cfg.ind_cb_hdlr); |
| 274 | err = bcmbal_msg_call(clone, |
| 275 | current_entry->cb_cfg.module, |
| 276 | _int_deliver_wrapper_cb, |
| 277 | BCMOS_MSG_SEND_AUTO_FREE); |
| 278 | if (BCM_ERR_OK != err) |
| 279 | { |
| 280 | BCM_LOG(ERROR, log_id_public_api, |
| 281 | "Couldn't deliver message for object %d to module %d. Error %s\n", |
| 282 | obj->obj_type, current_entry->cb_cfg.module, bcmos_strerror(err)); |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | return; |
| 289 | } |
| 290 | |
| 291 | /* |
| 292 | * This is the handler for indication messages received by the BAL Public API |
| 293 | * backend from the core. We need to see who has subscribed for these messages, |
| 294 | * and call those registered functions. |
| 295 | */ |
| 296 | static void bal_ipc_api_indication_handler(bcmos_module_id module_id, bcmos_msg *msg) |
| 297 | { |
| 298 | |
| 299 | void *msg_payload; |
| 300 | |
| 301 | msg_payload = bcmbal_payload_ptr_get(bcmbal_bal_hdr_get_by_bcmos_hdr(msg)); |
| 302 | |
| 303 | /* |
| 304 | * TO-DO |
| 305 | * validate the message major and minor version is correct |
| 306 | */ |
| 307 | |
| 308 | do |
| 309 | { |
| 310 | if(BAL_SUBSYSTEM_CORE != bcmbal_sender_get(msg_payload)) |
| 311 | { |
| 312 | BCM_LOG(ERROR, log_id_public_api, "Mgmt IND message received from wrong subsystem (%s)\n", |
| 313 | subsystem_str[bcmbal_sender_get(msg_payload)]); |
| 314 | break; |
| 315 | } |
| 316 | |
| 317 | if(BCMBAL_MGMT_API_IND_MSG != bcmbal_type_major_get(msg_payload)) |
| 318 | { |
| 319 | BCM_LOG(ERROR, log_id_public_api,"Mgmt IND message received with wrong major type (%d)\n", |
| 320 | bcmbal_type_major_get(msg_payload)); |
| 321 | break; |
| 322 | } |
| 323 | |
| 324 | /* Look through the list of registered subscribers for this indication |
| 325 | * and call them. |
| 326 | */ |
| 327 | BCM_LOG(DEBUG, log_id_public_api, |
| 328 | "Processing indication listeners\n"); |
| 329 | |
| 330 | process_listener_callbacks((bcmbal_obj *)msg_payload); |
| 331 | |
| 332 | } |
| 333 | while(0); |
| 334 | |
| 335 | bcmbal_msg_free(msg_payload); |
| 336 | |
| 337 | return; |
| 338 | } |
| 339 | |
| 340 | void enable_bal_api_indications(const char *balapi_mgmt_ip_port) |
| 341 | { |
| 342 | |
| 343 | bcmos_task_parm task_p = {}; |
| 344 | bcmos_module_parm module_p = {}; |
| 345 | bcmos_errno ret = BCM_ERR_OK; |
| 346 | mgmt_queue_addr_ports mgmt_queue_info; |
| 347 | |
| 348 | TAILQ_INIT(&indication_subscription_list); |
| 349 | |
| 350 | do |
| 351 | { |
| 352 | /* Create quues for communication between BAL API and the core */ |
| 353 | mgmt_queue_info.balapi_mgmt_ip_port = balapi_mgmt_ip_port; |
| 354 | ret = bal_api_ind_msg_queue_create(&mgmt_queue_info); |
| 355 | if (BCM_ERR_OK != ret) |
| 356 | { |
| 357 | BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API indication queue\n"); |
| 358 | break; |
| 359 | } |
| 360 | |
| 361 | module_p.qparm.name = "bal_api_ind_worker_module"; |
| 362 | module_p.init = _bal_worker_module_bal_api_init; |
| 363 | module_p.data = (long)&mgmt_queue_info; /* IP address and port information */ |
| 364 | |
| 365 | /* Create worker thread & modules for BAL indication messages from the core */ |
| 366 | task_p.name = "bal_api_ind_worker"; |
| 367 | task_p.priority = TASK_PRIORITY_WORKER; |
| 368 | |
| 369 | ret = bcmos_task_create(&api_ind_worker_thread, &task_p); |
| 370 | if (BCM_ERR_OK != ret) |
| 371 | { |
| 372 | BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API indication worker thread\n"); |
| 373 | break; |
| 374 | } |
| 375 | |
| 376 | ret = bcmos_module_create(BCMOS_MODULE_ID_WORKER_API_IND, &api_ind_worker_thread, &module_p); |
| 377 | if (ret) |
| 378 | { |
| 379 | BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API indication worker module\n"); |
| 380 | break; |
| 381 | } |
| 382 | |
| 383 | BCM_LOG(DEBUG, log_id_public_api, "BAL API indication handler registered\n"); |
| 384 | |
| 385 | } |
| 386 | while(0); |
| 387 | |
| 388 | return; |
| 389 | } |
| 390 | |
| 391 | |
| 392 | /** NOTE: when cb_cfg->obj_type is BCMBAL_OBJ_ID_ANY AND type is UN-SUBSCRIBE, then this is unsubscribe all */ |
| 393 | bcmos_errno _manage_api_ind_listener(bcmbal_ind_cb_management_type type, bcmbal_cb_cfg *cb_cfg) |
| 394 | { |
| 395 | |
| 396 | bcmos_errno ret = BCM_ERR_OK; |
| 397 | indication_subscription_inst *new_ind_entry; |
| 398 | indication_subscription_inst *current_entry, *p_temp_entry; |
| 399 | bcmos_bool is_unsubscribe; |
| 400 | |
| 401 | BUG_ON(NULL == cb_cfg); |
| 402 | BUG_ON(NULL == cb_cfg->ind_cb_hdlr); |
| 403 | |
| 404 | is_unsubscribe = (IND_CB_UNSUBSCRIBE == type); |
| 405 | |
| 406 | BCM_LOG(DEBUG,log_id_public_api, |
| 407 | "%s: %s for BAL API indications\n", |
| 408 | __FUNCTION__, |
| 409 | is_unsubscribe ? "Unsubscribing" : "Subscribing"); |
| 410 | |
| 411 | do |
| 412 | { |
| 413 | bcmos_bool b_found_existing_entry = BCMOS_FALSE; |
| 414 | |
| 415 | TAILQ_FOREACH_SAFE(current_entry, |
| 416 | &indication_subscription_list, |
| 417 | indication_subscription_inst_next, |
| 418 | p_temp_entry) |
| 419 | { |
| 420 | if( |
| 421 | ((is_unsubscribe && (BCMBAL_OBJ_ID_ANY == cb_cfg->obj_type)) ? BCMOS_TRUE : |
| 422 | (current_entry->cb_cfg.obj_type == cb_cfg->obj_type)) && |
| 423 | (current_entry->cb_cfg.ind_cb_hdlr == cb_cfg->ind_cb_hdlr) && |
| 424 | (current_entry->cb_cfg.module == cb_cfg->module)) |
| 425 | { |
| 426 | BCM_LOG(DEBUG,log_id_public_api, |
| 427 | "Found existing registration\n"); |
| 428 | |
| 429 | if(is_unsubscribe) |
| 430 | { |
| 431 | BCM_LOG(DEBUG,log_id_public_api, |
| 432 | "Removing registration\n"); |
| 433 | TAILQ_REMOVE(&indication_subscription_list, current_entry, indication_subscription_inst_next); |
| 434 | bcmos_free(current_entry); |
| 435 | } |
| 436 | |
| 437 | b_found_existing_entry = BCMOS_TRUE; |
| 438 | |
| 439 | /* Don't stop looking for matches if we are unsubscribing from ANY indications, there could be many |
| 440 | * assigned to a single callback function */ |
| 441 | if(!(is_unsubscribe && (BCMBAL_OBJ_ID_ANY == cb_cfg->obj_type))) break; |
| 442 | } |
| 443 | |
| 444 | } |
| 445 | |
| 446 | /* If we are subscribing to indication and we have already recorded |
| 447 | * this subscription, OR we are un-subscribing (whether or not we |
| 448 | * found a subscription to remove), then return right away with OK |
| 449 | */ |
| 450 | if((BCMOS_TRUE == b_found_existing_entry) || is_unsubscribe) |
| 451 | { |
| 452 | break; |
| 453 | } |
| 454 | |
| 455 | BCM_LOG(DEBUG,log_id_public_api, |
| 456 | "Registering NEW subscriber for BAL API indications\n"); |
| 457 | |
| 458 | /* This is a new subscription */ |
| 459 | new_ind_entry = bcmos_calloc(sizeof(indication_subscription_inst)); |
| 460 | |
| 461 | if (NULL == new_ind_entry) |
| 462 | { |
| 463 | BCM_LOG(FATAL, log_id_public_api, |
| 464 | "Failed to register api indication subscription\n"); |
| 465 | ret = BCM_ERR_NOMEM; |
| 466 | break; |
| 467 | } |
| 468 | |
| 469 | new_ind_entry->cb_cfg = *cb_cfg; |
| 470 | |
| 471 | TAILQ_INSERT_TAIL(&indication_subscription_list, new_ind_entry, indication_subscription_inst_next); |
| 472 | |
| 473 | } while(0); |
| 474 | |
| 475 | return ret; |
| 476 | |
| 477 | } |
| 478 | |
| 479 | static void free_all_indication_subscriptions(void) |
| 480 | { |
| 481 | indication_subscription_inst *current_entry, *p_temp_entry; |
| 482 | |
| 483 | TAILQ_FOREACH_SAFE(current_entry, |
| 484 | &indication_subscription_list, |
| 485 | indication_subscription_inst_next, |
| 486 | p_temp_entry) |
| 487 | { |
| 488 | TAILQ_REMOVE(&indication_subscription_list, current_entry, indication_subscription_inst_next); |
| 489 | bcmos_free(current_entry); |
| 490 | } |
| 491 | |
| 492 | return; |
| 493 | } |
| 494 | |
| 495 | |
| 496 | void bal_api_indications_finish(void) |
| 497 | { |
| 498 | free_all_indication_subscriptions(); |
| 499 | |
| 500 | bcmos_msg_unregister(BCMBAL_MGMT_API_IND_MSG, |
| 501 | 0, |
| 502 | BCMOS_MODULE_ID_WORKER_API_IND); |
| 503 | |
| 504 | bcmos_msg_queue_destroy(&bal_api_ind_backend_queue); |
| 505 | |
| 506 | bcmos_module_destroy(BCMOS_MODULE_ID_WORKER_API_IND); |
| 507 | |
| 508 | bcmos_task_destroy(&api_ind_rx_thread); |
| 509 | |
| 510 | bcmos_task_destroy(&api_ind_worker_thread); |
| 511 | |
| 512 | return; |
| 513 | } |