1/* 2 * Implementation of event notification component. 3 * 4 * Copyright (C) 2015, Broadcom Corporation 5 * All Rights Reserved. 6 * 7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; 8 * the contents of this file may not be disclosed to third parties, copied 9 * or duplicated in any form, in whole or in part, without the prior 10 * written permission of Broadcom Corporation. 11 * 12 * $Id$ 13 */ 14 15#include <bcm_cfg.h> 16#include <typedefs.h> 17#include <osl.h> 18#include <bcmutils.h> 19#include "bcm_notif_priv.h" 20#include <bcm_notif_pub.h> 21#include <bcm_mpool_pub.h> 22 23 24#if defined(BCMDBG) || defined(BCMDBG_ERR) 25#define NOTIF_ERROR(args) printf args 26#else 27#define NOTIF_ERROR(args) 28#endif 29 30 31static void dealloc_module(bcm_notif_module_t *notif_module); 32 33 34/* 35 * bcm_notif_attach() 36 * 37 * Notifier module attach-time initialization. 38 * 39 * Parameters: 40 * osh Operating system handle. 41 * mpm Memory pool manager handle. 42 * max_notif_servers Maximum number of supported servers. 43 * max_notif_clients Maximum number of supported clients. 44 * Returns: 45 * Global notifier module object. NULL on error. 46 */ 47bcm_notif_module_t* BCMATTACHFN(bcm_notif_attach)(osl_t *osh, 48 bcm_mpm_mgr_h mpm, 49 int max_notif_servers, 50 int max_notif_clients) 51{ 52 bcm_notif_module_t *notif_module; 53 int ret; 54 55 /* Allocate global notifier module state. */ 56 if ((notif_module = MALLOC(osh, sizeof(*notif_module))) == NULL) { 57 NOTIF_ERROR(("%s: out of mem, malloced %d bytes\n", 58 __FUNCTION__, MALLOCED(osh))); 59 goto fail; 60 } 61 62 /* Init global notifier module state. */ 63 memset(notif_module, 0, sizeof(*notif_module)); 64 notif_module->osh = osh; 65 notif_module->mpm = mpm; 66 67 /* Create memory pool for server objects. */ 68 ret = bcm_mpm_create_prealloc_pool(mpm, 69 sizeof(struct bcm_notif_list_struct), 70 max_notif_servers, NULL, 0, 71 "notif_s", ¬if_module->server_mem_pool); 72 if (ret != BCME_OK) { 73 goto fail; 74 } 75 76 /* Create memory pool for client objects. */ 77 ret = bcm_mpm_create_prealloc_pool(mpm, 78 sizeof(struct bcm_notif_client_request), 79 max_notif_clients, NULL, 0, 80 "notif_c", ¬if_module->client_mem_pool); 81 if (ret != BCME_OK) { 82 goto fail; 83 } 84 85 86 /* Success. */ 87 return (notif_module); 88 89fail: 90 dealloc_module(notif_module); 91 92 return (NULL); 93} 94 95/* 96 * bcm_notif_detach() 97 * 98 * Notifier module detach-time deinitialization. 99 * 100 * Parameters: 101 * notif_module Global notifier module object. 102 * Returns: 103 * Nothing. 104 */ 105void BCMATTACHFN(bcm_notif_detach)(bcm_notif_module_t *notif_module) 106{ 107 dealloc_module(notif_module); 108} 109 110 111/* 112 * dealloc_module() 113 * 114 * Helper function to perform common resource deallocation. 115 * 116 * Parameters: 117 * notif_module Global notifier module object. 118 * Returns: 119 * Nothing. 120 */ 121static void BCMATTACHFN(dealloc_module)(bcm_notif_module_t *notif_module) 122{ 123 if (notif_module != NULL) { 124 125 if (notif_module->client_mem_pool != NULL) { 126 bcm_mpm_delete_prealloc_pool(notif_module->mpm, 127 ¬if_module->client_mem_pool); 128 } 129 130 if (notif_module->server_mem_pool != NULL) { 131 bcm_mpm_delete_prealloc_pool(notif_module->mpm, 132 ¬if_module->server_mem_pool); 133 } 134 135 MFREE(notif_module->osh, notif_module, sizeof(*notif_module)); 136 } 137} 138 139 140/* 141 * bcm_notif_create_list() 142 * 143 * Initialize new list. Allocates memory. 144 * 145 * Parameters: 146 * notif_module Global notifier module object. 147 * hdlp Pointer to opaque list handle. 148 * Returns: 149 * BCME_OK Object initialized successfully. May be used. 150 * BCME_NOMEM Initialization failed due to no memory. Object must not be used 151 */ 152int BCMATTACHFN(bcm_notif_create_list)(bcm_notif_module_t *notif_module, bcm_notif_h *hdlp) 153{ 154 int result = BCME_OK; 155 156 if ((*hdlp = bcm_mp_alloc(notif_module->server_mem_pool)) == NULL) { 157 result = BCME_NOMEM; 158 } 159 else { 160 memset(*hdlp, 0, sizeof(**hdlp)); 161 (*hdlp)->allow_list_operations = TRUE; 162 (*hdlp)->notif_module = notif_module; 163 } 164 165 return (result); 166} 167 168/* 169 * bcm_notif_add_interest() 170 * 171 * Add an interested client 172 * 173 * Parameters 174 * hdl Opaque list handle. 175 * callback Client callback routine 176 * passthru Client pass-thru data 177 * Returns: 178 * BCME_OK Client interest added successfully 179 * BCME_NOMEM Add failed due to no memory. 180 * 181 */ 182int bcm_notif_add_interest(bcm_notif_h hdl, 183 bcm_notif_client_callback callback, 184 bcm_notif_client_data passthru) 185{ 186 int result = BCME_OK; 187 struct bcm_notif_client_request *new_node = NULL; 188 bcm_notif_module_t *notif_module = hdl->notif_module; 189 190 191 /* Ensure that list operations are not performed within client callbacks. */ 192 if (!(hdl->allow_list_operations)) 193 return (BCME_BUSY); 194 195 196 /* Allocate a new node for the new client interest request */ 197 if ((new_node = bcm_mp_alloc(notif_module->client_mem_pool)) == NULL) { 198 result = BCME_NOMEM; 199 } 200 else { 201 memset(new_node, 0, sizeof(*new_node)); 202 203 /* Initialize the new node with the client's information */ 204 new_node->callback = callback; 205 new_node->passthru = passthru; 206 /* Insert it at the tail of the linked list */ 207 if (hdl->tail == NULL) { 208 /* This is now the first node in the list. */ 209 new_node->next = new_node; 210 hdl->tail = new_node; 211 } 212 else { 213 /* This is not the first node in the list. */ 214 /* Insert as new tail in circular list. */ 215 new_node->next = hdl->tail->next; 216 hdl->tail->next = new_node; 217 hdl->tail = new_node; 218 } 219 } 220 221 return (result); 222} 223 224/* 225 * bcm_notif_remove_interest() 226 * 227 * Remove an interested client. The callback and passthru must be identical to the data 228 * supplied during registration. 229 * 230 * Parameters 231 * hdl Opaque list handle. 232 * callback Client callback routine 233 * passthru Client pass-thru data 234 * Returns: 235 * BCME_OK Client interest added successfully 236 * BCME_NOTFOUND Could not locate this specific client registration for removal. 237 * 238 */ 239int bcm_notif_remove_interest(bcm_notif_h hdl, 240 bcm_notif_client_callback callback, 241 bcm_notif_client_data passthru) 242{ 243 int result = BCME_NOTFOUND; 244 struct bcm_notif_client_request * nodep = hdl->tail; 245 bcm_notif_module_t *notif_module = hdl->notif_module; 246 247 /* Ensure that list operations are not performed within client callbacks. */ 248 if (!(hdl->allow_list_operations)) 249 return (BCME_BUSY); 250 251 252 if (nodep && nodep == nodep->next) { 253 /* 254 * Special case: list with just one node. 255 * If we get a hit, the list becomes empty because we delete the last node. 256 */ 257 if (nodep->callback == callback && nodep->passthru == passthru) { 258 bcm_mp_free(notif_module->client_mem_pool, nodep); 259 hdl->tail = NULL; 260 result = BCME_OK; 261 } 262 } else { 263 /* 264 * List with 0 or 2+ elements. 265 */ 266 struct bcm_notif_client_request * prevp; 267 while (nodep) { 268 prevp = nodep; 269 nodep = nodep->next; 270 if (nodep->callback == callback && nodep->passthru == passthru) { 271 /* 272 * If we got a hit at the tail of the list, need to adjust 273 * the main list "tail" pointer. 274 */ 275 if (hdl->tail == nodep) 276 hdl->tail = prevp; 277 result = BCME_OK; 278 prevp->next = nodep->next; 279 bcm_mp_free(notif_module->client_mem_pool, nodep); 280 /* Setting nodep to be 0 breaks us out of the loop. */ 281 nodep = 0; 282 } 283 } 284 } 285 286 return (result); 287} 288 289/* 290 * bcm_notif_signal() 291 * 292 * Notify all clients on an event list that the event has occured. Invoke their 293 * callbacks and provide both the server data and the client passthru data. 294 * 295 * Parameters 296 * hdl Opaque list handle. 297 * server_data Server data for the notification 298 * Returns: 299 * BCME_OK Client interest added successfully 300 * BCME_ERROR General error 301 */ 302int bcm_notif_signal(bcm_notif_h hdl, bcm_notif_server_data data) 303{ 304 int result = BCME_OK; 305 struct bcm_notif_client_request * nodep = hdl->tail; 306 307 /* Ensure that list operations are not performed within client callbacks. */ 308 if (!(hdl->allow_list_operations)) 309 return (BCME_BUSY); 310 311 312 if (nodep) { 313 struct bcm_notif_client_request * firstp; 314 nodep = nodep->next; 315 firstp = nodep; 316 317 /* Mark list to prevent against list operations within client callbacks. */ 318 hdl->allow_list_operations = FALSE; 319 320 do { 321 /* Signal the current client */ 322 nodep->callback(nodep->passthru, data); 323 /* Advance to next client registration */ 324 nodep = nodep->next; 325 } while (nodep != firstp); 326 327 /* Done client callbacks - allow list operations again. */ 328 hdl->allow_list_operations = TRUE; 329 } 330 331 return (result); 332} 333 334/* 335 * bcm_notif_delete_list() 336 * 337 * Remove all the nodes and the list itself. 338 * 339 * Parameters 340 * hdlp Pointer to opaque list handle. 341 * Returns: 342 * BCME_OK Event list successfully deleted. 343 * BCME_ERROR General error 344 */ 345int BCMATTACHFN(bcm_notif_delete_list)(bcm_notif_h *hdl) 346{ 347 /* First free all the nodes. */ 348 struct bcm_notif_client_request * nodep = (*hdl)->tail; 349 struct bcm_notif_client_request * prevp; 350 bcm_notif_module_t *notif_module = (*hdl)->notif_module; 351 352 /* Ensure that list operations are not performed within client callbacks. */ 353 if (!((*hdl)->allow_list_operations)) 354 return (BCME_BUSY); 355 356 357 while (nodep) { 358 prevp = nodep; 359 nodep = nodep->next; 360 361 if (prevp == nodep) { 362 /* Deleting the last node. Special case. */ 363 bcm_mp_free(notif_module->client_mem_pool, nodep); 364 nodep = prevp = NULL; 365 } else { 366 prevp->next = nodep->next; 367 bcm_mp_free(notif_module->client_mem_pool, nodep); 368 nodep = prevp->next; 369 } 370 } 371 372 /* Free the list itself in addition to the nodes. */ 373 memset(*hdl, 0, sizeof(**hdl)); 374 bcm_mp_free(notif_module->server_mem_pool, *hdl); 375 *hdl = NULL; 376 377 return (BCME_OK); 378} 379 380/* 381 * bcm_notif_dump_list() 382 * 383 * For debugging, display interest list 384 * 385 * Parameters 386 * hdl Opaque list handle. 387 * b Output buffer. 388 * Returns: 389 * BCME_OK Event list successfully dumped. 390 * BCME_ERROR General error. 391 */ 392int bcm_notif_dump_list(bcm_notif_h hdl, struct bcmstrbuf *b) 393{ 394#if defined(BCMDBG) 395 struct bcm_notif_client_request * nodep; 396 397 if (hdl == NULL) 398 bcm_bprintf(b, "<uninit list>\n"); 399 else { 400 nodep = hdl->tail; 401 402 bcm_bprintf(b, "List id=0x%p: ", hdl); 403 404 if (nodep == NULL) { 405 bcm_bprintf(b, "(empty)"); 406 } else { 407 /* List is not empty. Display all data in correct sequence. */ 408 struct bcm_notif_client_request * firstp = hdl->tail->next; 409 nodep = nodep->next; 410 do { 411 bcm_bprintf(b, " [0x%p,0x%p]", nodep->callback, 412 nodep->passthru); 413 nodep = nodep->next; 414 } while (nodep != firstp); 415 } 416 } 417 418 bcm_bprintf(b, "\n"); 419#endif 420 421 return (BCME_OK); 422} 423