1/*
2* Copyright (c) 2004
3*	Hartmut Brandt
4*	All rights reserved.
5*
6* Author: Harti Brandt <harti@freebsd.org>
7*
8* Redistribution of this software and documentation and use in source and
9* binary forms, with or without modification, are permitted provided that
10* the following conditions are met:
11*
12* 1. Redistributions of source code or documentation must retain the above
13*    copyright notice, this list of conditions and the following disclaimer.
14* 2. Redistributions in binary form must reproduce the above copyright
15*    notice, this list of conditions and the following disclaimer in the
16*    documentation and/or other materials provided with the distribution.
17*
18* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
19* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
22* THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
23* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*
30* $Begemot: libunimsg/netnatm/api/cc_sig.c,v 1.1 2004/07/08 08:21:54 brandt Exp $
31*
32* ATM API as defined per af-saa-0108
33*
34* Generic signal handling
35*/
36#include <netnatm/unimsg.h>
37#include <netnatm/msg/unistruct.h>
38#include <netnatm/msg/unimsglib.h>
39#include <netnatm/api/unisap.h>
40#include <netnatm/sig/unidef.h>
41#include <netnatm/api/atmapi.h>
42#include <netnatm/api/ccatm.h>
43#include <netnatm/api/ccpriv.h>
44
45enum {
46	SIG_USER,
47	SIG_CONN,
48};
49
50struct ccsig {
51	u_char	type;		/* type of target */
52	u_char	has_msg;	/* arg1 is a message */
53	void	*target;	/* target instance */
54	u_int	sig;		/* signal */
55	void	*arg1;		/* argument */
56	u_int	arg2;		/* argument */
57	TAILQ_ENTRY(ccsig) link;
58};
59
60#if defined(__GNUC__) && __GNUC__ < 3
61#define	cc_sig_log(CC, FMT, ARGS...) do {				\
62	if ((CC)->log & CCLOG_SIGS)					\
63		(CC)->funcs->log("%s: " FMT, __FUNCTION__ , ## ARGS);	\
64    } while (0)
65#else
66#define	cc_sig_log(CC, FMT, ...) do {					\
67	if ((CC)->log & CCLOG_SIGS)					\
68		(CC)->funcs->log("%s: " FMT, __func__, __VA_ARGS__);	\
69    } while (0)
70#endif
71
72
73const char *const cc_user_sigtab[] = {
74#define	DEF(N) [USER_SIG_##N] = #N,
75USER_SIGS
76#undef DEF
77};
78
79const char *const cc_conn_sigtab[] = {
80#define	DEF(N) [CONN_SIG_##N] = #N,
81CONN_SIGS
82#undef DEF
83};
84
85
86/*
87 * Allocate and populate a signal
88 */
89static /* __inline */ struct ccsig *
90sig_alloc(struct ccdata *cc, u_int type, void *target, u_int has_msg,
91    u_int sig, void *arg1, u_int arg2)
92{
93	struct ccsig *s;
94
95	if ((s = TAILQ_FIRST(&cc->free_sigs)) == NULL) {
96		s = CCZALLOC(sizeof(struct ccsig));
97		if (s == NULL) {
98			cc_log(cc, "signal %u/%u lost - ENOMEM", type, sig);
99			return (NULL);
100		}
101	} else
102		TAILQ_REMOVE(&cc->free_sigs, s, link);
103
104	s->type = type;
105	s->has_msg = has_msg;
106	s->target = target;
107	s->sig = sig;
108	s->arg1 = arg1;
109	s->arg2 = arg2;
110
111	return (s);
112}
113
114/*
115 * Queue a signal to this user
116 */
117int
118cc_user_sig(struct ccuser *user, enum user_sig sig, void *arg1, u_int arg2)
119{
120	struct ccsig *s;
121
122	s = sig_alloc(user->cc, SIG_USER, user, 0, sig, arg1, arg2);
123	if (s == NULL)
124		return (ENOMEM);
125	TAILQ_INSERT_TAIL(&user->cc->sigs, s, link);
126	cc_sig_log(user->cc, "queuing sig %s to user %p", cc_user_sigtab[sig],
127	    user);
128	return (0);
129}
130
131/* Queue a signal with message to this user */
132int
133cc_user_sig_msg(struct ccuser *user, enum user_sig sig, struct uni_msg *msg)
134{
135	struct ccsig *s;
136
137	s = sig_alloc(user->cc, SIG_USER, user, msg != NULL, sig, msg, 0);
138	if (s == NULL)
139		return (ENOMEM);
140	TAILQ_INSERT_TAIL(&user->cc->sigs, s, link);
141	cc_sig_log(user->cc, "queuing sig %s to user %p", cc_user_sigtab[sig],
142	    user);
143	return (0);
144}
145
146/*
147 * Signal to connection
148 */
149static int
150sig_conn(struct ccconn *conn, enum conn_sig sig, u_int has_msg, void *arg)
151{
152	struct ccsig *s;
153	const struct ccreq *r = NULL;
154
155	s = sig_alloc(conn->cc, SIG_CONN, conn, has_msg, sig, arg, 0);
156	if (s == NULL)
157		return (ENOMEM);
158
159	if (conn->port != NULL) {
160		/* argh */
161		TAILQ_FOREACH(r, &conn->port->cookies, link)
162			if (r->conn == conn)
163				break;
164	}
165	if (r == NULL) {
166		TAILQ_INSERT_TAIL(&conn->cc->sigs, s, link);
167		cc_sig_log(conn->cc, "queuing sig %s to conn %p",
168		    cc_conn_sigtab[sig], conn);
169	} else {
170		TAILQ_INSERT_TAIL(&conn->cc->def_sigs, s, link);
171		cc_sig_log(conn->cc, "queuing defered sig %s to conn %p",
172		    cc_conn_sigtab[sig], conn);
173	}
174	return (0);
175}
176
177/*
178 * Queue a signal to a connection.
179 */
180int
181cc_conn_sig(struct ccconn *conn, enum conn_sig sig, void *arg1)
182{
183
184	return (sig_conn(conn, sig, 0, arg1));
185}
186
187/*
188 * signal with message to connection
189 */
190int
191cc_conn_sig_msg(struct ccconn *conn, enum conn_sig sig, struct uni_msg *msg)
192{
193
194	return (sig_conn(conn, sig, (msg != NULL), msg));
195}
196int
197cc_conn_sig_msg_nodef(struct ccconn *conn, enum conn_sig sig,
198    struct uni_msg *msg)
199{
200	struct ccsig *s;
201
202	s = sig_alloc(conn->cc, SIG_CONN, conn, (msg != NULL), sig, msg, 0);
203	if (s == NULL)
204		return (ENOMEM);
205
206	TAILQ_INSERT_TAIL(&conn->cc->sigs, s, link);
207	cc_sig_log(conn->cc, "queuing sig %s to conn %p",
208	    cc_conn_sigtab[sig], conn);
209
210	return (0);
211}
212
213/*
214 * Queue a response signal to a connection.
215 */
216int
217cc_conn_resp(struct ccconn *conn, enum conn_sig sig, u_int cookie __unused,
218    u_int reason, u_int state)
219{
220	struct ccsig *s, *s1, *s2;
221
222	s = sig_alloc(conn->cc, SIG_CONN, conn, 0, sig, NULL,
223	     ((reason & 0xffff) << 16) | (state & 0xffff));
224	if (s == NULL)
225		return (ENOMEM);
226
227	TAILQ_INSERT_TAIL(&conn->cc->sigs, s, link);
228
229	cc_sig_log(conn->cc, "queuing response %s to conn %p",
230	    cc_conn_sigtab[sig], conn);
231
232	s1 = TAILQ_FIRST(&conn->cc->def_sigs);
233	while (s1 != NULL) {
234		s2 = TAILQ_NEXT(s1, link);
235		if (s1->type == SIG_CONN && s1->target == conn) {
236			TAILQ_REMOVE(&conn->cc->def_sigs, s1, link);
237			TAILQ_INSERT_AFTER(&conn->cc->sigs, s, s1, link);
238			cc_sig_log(conn->cc, "undefering sig %s to conn %p",
239			    cc_conn_sigtab[s1->sig], conn);
240			s = s1;
241		}
242		s1 = s2;
243	}
244
245	return (0);
246}
247
248/*
249 * Flush all signals to a given target from both queues
250 */
251static /* __inline */ void
252sig_flush(struct ccdata *cc, u_int type, void *target)
253{
254	struct ccsig *s, *s1;
255
256	s = TAILQ_FIRST(&cc->sigs);
257	while (s != NULL) {
258		s1 = TAILQ_NEXT(s, link);
259		if (s->type == type && s->target == target) {
260			if (s->has_msg)
261				uni_msg_destroy((struct uni_msg *)s->arg1);
262			TAILQ_REMOVE(&cc->sigs, s, link);
263			TAILQ_INSERT_HEAD(&cc->free_sigs, s, link);
264		}
265		s = s1;
266	}
267
268	s = TAILQ_FIRST(&cc->def_sigs);
269	while (s != NULL) {
270		s1 = TAILQ_NEXT(s, link);
271		if (s->type == type && s->target == target) {
272			if (s->has_msg)
273				uni_msg_destroy((struct uni_msg *)s->arg1);
274			TAILQ_REMOVE(&cc->def_sigs, s, link);
275			TAILQ_INSERT_HEAD(&cc->free_sigs, s, link);
276		}
277		s = s1;
278	}
279}
280
281/*
282 * Flush all signals to this user
283 */
284void
285cc_user_sig_flush(struct ccuser *user)
286{
287
288	cc_sig_log(user->cc, "flushing signals to user %p", user);
289	sig_flush(user->cc, SIG_USER, user);
290}
291
292/*
293 * Flush all signals to this connection
294 */
295void
296cc_conn_sig_flush(struct ccconn *conn)
297{
298
299	cc_sig_log(conn->cc, "flushing signals to conn %p", conn);
300	sig_flush(conn->cc, SIG_CONN, conn);
301}
302
303/*
304 * Do the work
305 */
306void
307cc_work(struct ccdata *cc)
308{
309	struct ccsig *s;
310
311	cc_sig_log(cc, "start %s", "work");
312	while ((s = TAILQ_FIRST(&cc->sigs)) != NULL) {
313		TAILQ_REMOVE(&cc->sigs, s, link);
314		if (s->type == SIG_USER)
315			cc_user_sig_handle(s->target, s->sig, s->arg1, s->arg2);
316		else {
317			cc_conn_sig_handle(s->target, s->sig, s->arg1, s->arg2);
318			if (s->has_msg)
319				uni_msg_destroy(s->arg1);
320		}
321		TAILQ_INSERT_HEAD(&cc->free_sigs, s, link);
322	}
323	cc_sig_log(cc, "end %s", "work");
324}
325
326/*
327 * flush all signals
328 */
329void
330cc_sig_flush_all(struct ccdata *cc)
331{
332	struct ccsig *s;
333
334	while ((s = TAILQ_FIRST(&cc->sigs)) != NULL) {
335		if (s->has_msg)
336			uni_msg_destroy((struct uni_msg *)s->arg1);
337		TAILQ_REMOVE(&cc->sigs, s, link);
338		CCFREE(s);
339	}
340	while ((s = TAILQ_FIRST(&cc->def_sigs)) != NULL) {
341		if (s->has_msg)
342			uni_msg_destroy((struct uni_msg *)s->arg1);
343		TAILQ_REMOVE(&cc->def_sigs, s, link);
344		CCFREE(s);
345	}
346	while ((s = TAILQ_FIRST(&cc->free_sigs)) != NULL) {
347		TAILQ_REMOVE(&cc->free_sigs, s, link);
348		CCFREE(s);
349	}
350}
351