blob: 178060c60b9b90593377cf35df8fef9dee59919a [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/******************************************************************************
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 */
59static bcmos_task api_ind_rx_thread;
60static bcmos_task api_ind_worker_thread;
61
62/* Local function declarations */
63static int _bal_ipc_api_ind_rx_handler(long data);
64static void bal_ipc_api_indication_handler(bcmos_module_id module_id, bcmos_msg *msg);
65
66
67typedef struct indication_subscription_inst indication_subscription_inst;
68struct 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
75TAILQ_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 */
81static bcmos_msg_queue bal_api_ind_backend_queue;
82bcmos_msg_queue *p_bal_api_ind_queue;
83
84/* Create API backend message queue */
85bcmos_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 */
148static 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
182static 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 */
226static 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
235static 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 */
296static 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
340void 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 */
393bcmos_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
479static 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
496void 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}