1/*	$NetBSD: i4b_l4mgmt.c,v 1.17 2007/07/09 21:11:14 ad Exp $	*/
2
3/*
4 * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *---------------------------------------------------------------------------
28 *
29 *	i4b_l4mgmt.c - layer 4 calldescriptor management utilites
30 *	-----------------------------------------------------------
31 *
32 *	$Id: i4b_l4mgmt.c,v 1.18 2010/01/18 16:37:41 pooka Exp $
33 *
34 * $FreeBSD$
35 *
36 *      last edit-date: [Fri Jan  5 11:33:47 2001]
37 *
38 *---------------------------------------------------------------------------*/
39
40#include <sys/cdefs.h>
41__KERNEL_RCSID(0, "$NetBSD: i4b_l4mgmt.c,v 1.17 2007/07/09 21:11:14 ad Exp $");
42
43#include "isdn.h"
44
45#if NISDN > 0
46
47#include <sys/param.h>
48#include <sys/kernel.h>
49#include <sys/systm.h>
50#include <sys/mbuf.h>
51#include <sys/socket.h>
52#include <net/if.h>
53
54#if defined(__NetBSD__) && __NetBSD_Version__ >= 104230000
55#include <sys/callout.h>
56#endif
57
58#if defined(__FreeBSD__)
59#if defined (__FreeBSD_version) && __FreeBSD_version <= 400000
60#include <machine/random.h>
61#else
62#include <sys/random.h>
63#endif
64#endif
65
66#ifdef __FreeBSD__
67#include <machine/i4b_debug.h>
68#include <machine/i4b_ioctl.h>
69#else
70#include <netisdn/i4b_debug.h>
71#include <netisdn/i4b_ioctl.h>
72#endif
73
74#include <netisdn/i4b_l3l4.h>
75#include <netisdn/i4b_mbuf.h>
76#include <netisdn/i4b_isdnq931.h>
77#include <netisdn/i4b_global.h>
78
79#include <netisdn/i4b_l2.h>
80#include <netisdn/i4b_l1l2.h>
81#include <netisdn/i4b_l4.h>
82
83static unsigned int get_cdid(void);
84
85static void i4b_init_callout(call_desc_t *);
86static void i4b_stop_callout(call_desc_t *cd);
87
88call_desc_t call_desc[N_CALL_DESC];	/* call descriptor array */
89int num_call_desc = 0;
90
91/*---------------------------------------------------------------------------*
92 *      return a new unique call descriptor id
93 *	--------------------------------------
94 *	returns a new calldescriptor id which is used to uniquely identyfy
95 *	a single call in the communication between kernel and userland.
96 *	this cdid is then used to associate a calldescriptor with an id.
97 *---------------------------------------------------------------------------*/
98static unsigned int
99get_cdid(void)
100{
101	static unsigned int cdid_count = 0;
102	int i;
103	int x;
104
105	x = splnet();
106
107	/* get next id */
108
109	cdid_count++;
110
111again:
112	if(cdid_count == CDID_UNUSED)		/* zero is invalid */
113		cdid_count++;
114	else if(cdid_count > CDID_MAX)		/* wraparound ? */
115		cdid_count = 1;
116
117	/* check if id already in use */
118
119	for(i=0; i < num_call_desc; i++)
120	{
121		if(call_desc[i].cdid == cdid_count)
122		{
123			cdid_count++;
124			goto again;
125		}
126	}
127
128	splx(x);
129
130	return(cdid_count);
131}
132
133/*---------------------------------------------------------------------------*
134 *      reserve a calldescriptor for later usage
135 *      ----------------------------------------
136 *      searches the calldescriptor array until an unused
137 *      descriptor is found, gets a new calldescriptor id
138 *      and reserves it by putting the id into the cdid field.
139 *      returns pointer to the calldescriptor.
140 *---------------------------------------------------------------------------*/
141call_desc_t *
142reserve_cd(void)
143{
144	call_desc_t *cd;
145	int x;
146	int i;
147
148	x = splnet();
149
150	cd = NULL;
151
152	for(i=0; i < num_call_desc; i++)
153	{
154		if(call_desc[i].cdid == CDID_UNUSED)
155		{
156			cd = &(call_desc[i]);	/* get pointer to descriptor */
157			NDBGL4(L4_MSG, "found free cd - index=%d cdid=%u",
158				 i, call_desc[i].cdid);
159			break;
160		}
161	}
162	if (cd == NULL && num_call_desc < N_CALL_DESC) {
163		i = num_call_desc++;
164		cd = &(call_desc[i]);	/* get pointer to descriptor */
165		NDBGL4(L4_MSG, "found free cd - index=%d cdid=%u",
166			 i, call_desc[i].cdid);
167	}
168	if (cd != NULL) {
169		memset(cd, 0, sizeof(call_desc_t)); /* clear it */
170		cd->cdid = get_cdid();	/* fill in new cdid */
171	}
172
173	splx(x);
174
175	if(cd == NULL)
176		panic("reserve_cd: no free call descriptor available!");
177
178	i4b_init_callout(cd);
179
180	return(cd);
181}
182
183/*---------------------------------------------------------------------------*
184 *      free a calldescriptor
185 *      ---------------------
186 *      free a unused calldescriptor by giving address of calldescriptor
187 *      and writing a 0 into the cdid field marking it as unused.
188 *---------------------------------------------------------------------------*/
189void
190freecd_by_cd(call_desc_t *cd)
191{
192	int i;
193	int x = splnet();
194
195	for(i=0; i < num_call_desc; i++)
196	{
197		if( (call_desc[i].cdid != CDID_UNUSED) &&
198		    (&(call_desc[i]) == cd) )
199		{
200			NDBGL4(L4_MSG, "releasing cd - index=%d cdid=%u cr=%d",
201				i, call_desc[i].cdid, cd->cr);
202			call_desc[i].cdid = CDID_UNUSED;
203			break;
204		}
205	}
206
207	if(i == N_CALL_DESC)
208		panic("freecd_by_cd: ERROR, cd not found, cr = %d", cd->cr);
209
210	splx(x);
211}
212
213/*
214 * ISDN is gone, get rid of all CDs for it
215 */
216void free_all_cd_of_isdnif(int isdnif)
217{
218	int i;
219	int x = splnet();
220
221	for(i=0; i < num_call_desc; i++)
222	{
223		if( (call_desc[i].cdid != CDID_UNUSED) &&
224		    call_desc[i].isdnif == isdnif) {
225			NDBGL4(L4_MSG, "releasing cd - index=%d cdid=%u cr=%d",
226				i, call_desc[i].cdid, call_desc[i].cr);
227			if (call_desc[i].callouts_inited)
228				i4b_stop_callout(&call_desc[i]);
229			call_desc[i].cdid = CDID_UNUSED;
230			call_desc[i].isdnif = -1;
231			call_desc[i].l3drv = NULL;
232		}
233	}
234
235	splx(x);
236}
237
238/*---------------------------------------------------------------------------*
239 *      return pointer to calldescriptor by giving the calldescriptor id
240 *      ----------------------------------------------------------------
241 *      lookup a calldescriptor in the calldescriptor array by looking
242 *      at the cdid field. return pointer to calldescriptor if found,
243 *      else return NULL if not found.
244 *---------------------------------------------------------------------------*/
245call_desc_t *
246cd_by_cdid(unsigned int cdid)
247{
248	int i;
249
250	for(i=0; i < num_call_desc; i++)
251	{
252		if(call_desc[i].cdid == cdid)
253		{
254			NDBGL4(L4_MSG, "found cdid - index=%d cdid=%u cr=%d",
255					i, call_desc[i].cdid, call_desc[i].cr);
256			i4b_init_callout(&call_desc[i]);
257			return(&(call_desc[i]));
258		}
259	}
260	return(NULL);
261}
262
263/*---------------------------------------------------------------------------*
264 *      search calldescriptor
265 *      ---------------------
266 *      This routine searches for the calldescriptor for a passive controller
267 *      given by unit number, callreference and callreference flag.
268 *	It returns a pointer to the calldescriptor if found, else a NULL.
269 *---------------------------------------------------------------------------*/
270call_desc_t *
271cd_by_isdnifcr(int isdnif, int cr, int crf)
272{
273	int i;
274
275	for(i=0; i < num_call_desc; i++) {
276		if (call_desc[i].cdid != CDID_UNUSED
277		    && call_desc[i].isdnif == isdnif
278		    && call_desc[i].cr == cr
279		    && call_desc[i].crflag == crf) {
280			NDBGL4(L4_MSG, "found cd, index=%d cdid=%u cr=%d",
281			    i, call_desc[i].cdid, call_desc[i].cr);
282			i4b_init_callout(&call_desc[i]);
283			return(&(call_desc[i]));
284		}
285	}
286	return(NULL);
287}
288
289/*---------------------------------------------------------------------------*
290 *	generate 7 bit "random" number used for outgoing Call Reference
291 *---------------------------------------------------------------------------*/
292unsigned char
293get_rand_cr(int unit)
294{
295	register int i, j;
296	static u_char val, retval;
297	static int called = 42;
298	struct timeval t;
299
300	val += ++called;
301
302	for(i=0; i < 50 ; i++, val++)
303	{
304		int found = 1;
305
306#if defined(__FreeBSD__)
307
308#ifdef RANDOMDEV
309		read_random((char *)&val, sizeof(val));
310#else
311		val = (u_char)random();
312#endif /* RANDOMDEV */
313
314#else
315		getmicrotime(&t);
316		val |= unit+i;
317		val <<= i;
318		val ^= (t.tv_sec >> 8) ^ t.tv_usec;
319		val <<= i;
320		val ^= t.tv_sec ^ (t.tv_usec >> 8);
321#endif
322
323		retval = val & 0x7f;
324
325		if(retval == 0 || retval == 0x7f)
326			continue;
327
328		for(j=0; j < num_call_desc; j++)
329		{
330			if( (call_desc[j].cdid != CDID_UNUSED) &&
331			    (call_desc[j].cr == retval) )
332			{
333				found = 0;
334				break;
335			}
336		}
337
338		if(found)
339			return(retval);
340	}
341	return(0);	/* XXX */
342}
343
344static void
345i4b_stop_callout(call_desc_t *cd)
346{
347	if (!cd->callouts_inited)
348		return;
349
350	callout_stop(&cd->idle_timeout_handle);
351	callout_stop(&cd->T303_callout);
352	callout_stop(&cd->T305_callout);
353	callout_stop(&cd->T308_callout);
354	callout_stop(&cd->T309_callout);
355	callout_stop(&cd->T310_callout);
356	callout_stop(&cd->T313_callout);
357	callout_stop(&cd->T400_callout);
358}
359
360/*---------------------------------------------------------------------------*
361 *	initialize the callout handles for FreeBSD
362 *---------------------------------------------------------------------------*/
363void
364i4b_init_callout(call_desc_t *cd)
365{
366	if(cd->callouts_inited == 0)
367	{
368		callout_init(&cd->idle_timeout_handle, 0);
369		callout_init(&cd->T303_callout, 0);
370		callout_init(&cd->T305_callout, 0);
371		callout_init(&cd->T308_callout, 0);
372		callout_init(&cd->T309_callout, 0);
373		callout_init(&cd->T310_callout, 0);
374		callout_init(&cd->T313_callout, 0);
375		callout_init(&cd->T400_callout, 0);
376		cd->callouts_inited = 1;
377	}
378}
379
380#endif /* NISDN > 0 */
381