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", &notif_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", &notif_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			                             &notif_module->client_mem_pool);
128		}
129
130		if (notif_module->server_mem_pool != NULL) {
131			bcm_mpm_delete_prealloc_pool(notif_module->mpm,
132			                             &notif_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