Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/isdn/hardware/eicon/capifunc.c b/drivers/isdn/hardware/eicon/capifunc.c
new file mode 100644
index 0000000..0afd763
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capifunc.c
@@ -0,0 +1,1219 @@
+/* $Id: capifunc.c,v 1.61.4.7 2005/02/11 19:40:25 armin Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface common functions
+ * 
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "platform.h"
+#include "os_capi.h"
+#include "di_defs.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "divasync.h"
+#include "capifunc.h"
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+DIVA_CAPI_ADAPTER *adapter = (DIVA_CAPI_ADAPTER *) NULL;
+APPL *application = (APPL *) NULL;
+byte max_appl = MAX_APPL;
+byte max_adapter = 0;
+static CAPI_MSG *mapped_msg = (CAPI_MSG *) NULL;
+
+byte UnMapController(byte);
+char DRIVERRELEASE_CAPI[32];
+
+extern void AutomaticLaw(DIVA_CAPI_ADAPTER *);
+extern void callback(ENTITY *);
+extern word api_remove_start(void);
+extern word CapiRelease(word);
+extern word CapiRegister(word);
+extern word api_put(APPL *, CAPI_MSG *);
+
+static diva_os_spin_lock_t api_lock;
+
+static LIST_HEAD(cards);
+
+static dword notify_handle;
+static void DIRequest(ENTITY * e);
+static DESCRIPTOR MAdapter;
+static DESCRIPTOR DAdapter;
+static byte ControllerMap[MAX_DESCRIPTORS + 1];
+
+
+static void diva_register_appl(struct capi_ctr *, __u16,
+			       capi_register_params *);
+static void diva_release_appl(struct capi_ctr *, __u16);
+static char *diva_procinfo(struct capi_ctr *);
+static u16 diva_send_message(struct capi_ctr *,
+			     diva_os_message_buffer_s *);
+extern void diva_os_set_controller_struct(struct capi_ctr *);
+
+extern void DIVA_DIDD_Read(DESCRIPTOR *, int);
+
+/*
+ * debug
+ */
+static void no_printf(unsigned char *, ...);
+#include "debuglib.c"
+static void xlog(char *x, ...)
+{
+#ifndef DIVA_NO_DEBUGLIB
+	va_list ap;
+	if (myDriverDebugHandle.dbgMask & DL_XLOG) {
+		va_start(ap, x);
+		if (myDriverDebugHandle.dbg_irq) {
+			myDriverDebugHandle.dbg_irq(myDriverDebugHandle.id,
+						    DLI_XLOG, x, ap);
+		} else if (myDriverDebugHandle.dbg_old) {
+			myDriverDebugHandle.dbg_old(myDriverDebugHandle.id,
+						    x, ap);
+		}
+		va_end(ap);
+	}
+#endif
+}
+
+/*
+ * info for proc
+ */
+static char *diva_procinfo(struct capi_ctr *ctrl)
+{
+	return (ctrl->serial);
+}
+
+/*
+ * stop debugging
+ */
+static void stop_dbg(void)
+{
+	DbgDeregister();
+	memset(&MAdapter, 0, sizeof(MAdapter));
+	dprintf = no_printf;
+}
+
+/*
+ * dummy debug function
+ */
+static void no_printf(unsigned char *x, ...)
+{
+}
+
+/*
+ * Controller mapping
+ */
+byte MapController(byte Controller)
+{
+	byte i;
+	byte MappedController = 0;
+	byte ctrl = Controller & 0x7f;	/* mask external controller bit off */
+
+	for (i = 1; i < max_adapter + 1; i++) {
+		if (ctrl == ControllerMap[i]) {
+			MappedController = (byte) i;
+			break;
+		}
+	}
+	if (i > max_adapter) {
+		ControllerMap[0] = ctrl;
+		MappedController = 0;
+	}
+	return (MappedController | (Controller & 0x80));	/* put back external controller bit */
+}
+
+/*
+ * Controller unmapping
+ */
+byte UnMapController(byte MappedController)
+{
+	byte Controller;
+	byte ctrl = MappedController & 0x7f;	/* mask external controller bit off */
+
+	if (ctrl <= max_adapter) {
+		Controller = ControllerMap[ctrl];
+	} else {
+		Controller = 0;
+	}
+
+	return (Controller | (MappedController & 0x80));	/* put back external controller bit */
+}
+
+/*
+ * find a new free id
+ */
+static int find_free_id(void)
+{
+	int num = 0;
+	DIVA_CAPI_ADAPTER *a;
+
+	while (num < MAX_DESCRIPTORS) {
+		a = &adapter[num];
+		if (!a->Id)
+			break;
+		num++;
+	}
+	return(num + 1);
+}
+
+/*
+ * find a card structure by controller number
+ */
+static diva_card *find_card_by_ctrl(word controller)
+{
+	struct list_head *tmp;
+	diva_card *card;
+
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, diva_card, list);
+		if (ControllerMap[card->Id] == controller) {
+			if (card->remove_in_progress)
+				card = NULL;
+			return(card);
+		}
+	}
+	return (diva_card *) 0;
+}
+
+/*
+ * Buffer RX/TX 
+ */
+void *TransmitBufferSet(APPL * appl, dword ref)
+{
+	appl->xbuffer_used[ref] = TRUE;
+	DBG_PRV1(("%d:xbuf_used(%d)", appl->Id, ref + 1))
+	    return (void *) ref;
+}
+
+void *TransmitBufferGet(APPL * appl, void *p)
+{
+	if (appl->xbuffer_internal[(dword) p])
+		return appl->xbuffer_internal[(dword) p];
+
+	return appl->xbuffer_ptr[(dword) p];
+}
+
+void TransmitBufferFree(APPL * appl, void *p)
+{
+	appl->xbuffer_used[(dword) p] = FALSE;
+	DBG_PRV1(("%d:xbuf_free(%d)", appl->Id, ((dword) p) + 1))
+}
+
+void *ReceiveBufferGet(APPL * appl, int Num)
+{
+	return &appl->ReceiveBuffer[Num * appl->MaxDataLength];
+}
+
+/*
+ * api_remove_start/complete for cleanup
+ */
+void api_remove_complete(void)
+{
+	DBG_PRV1(("api_remove_complete"))
+}
+
+/*
+ * main function called by message.c
+ */
+void sendf(APPL * appl, word command, dword Id, word Number, byte * format, ...)
+{
+	word i, j;
+	word length = 12, dlength = 0;
+	byte *write;
+	CAPI_MSG msg;
+	byte *string = NULL;
+	va_list ap;
+	diva_os_message_buffer_s *dmb;
+	diva_card *card = NULL;
+	dword tmp;
+
+	if (!appl)
+		return;
+
+	DBG_PRV1(("sendf(a=%d,cmd=%x,format=%s)",
+		  appl->Id, command, (byte *) format))
+
+	PUT_WORD(&msg.header.appl_id, appl->Id);
+	PUT_WORD(&msg.header.command, command);
+	if ((byte) (command >> 8) == 0x82)
+		Number = appl->Number++;
+	PUT_WORD(&msg.header.number, Number);
+
+	PUT_DWORD(&msg.header.controller, Id);
+	write = (byte *) & msg;
+	write += 12;
+
+	va_start(ap, format);
+	for (i = 0; format[i]; i++) {
+		switch (format[i]) {
+		case 'b':
+			tmp = va_arg(ap, dword);
+			*(byte *) write = (byte) (tmp & 0xff);
+			write += 1;
+			length += 1;
+			break;
+		case 'w':
+			tmp = va_arg(ap, dword);
+			PUT_WORD(write, (tmp & 0xffff));
+			write += 2;
+			length += 2;
+			break;
+		case 'd':
+			tmp = va_arg(ap, dword);
+			PUT_DWORD(write, tmp);
+			write += 4;
+			length += 4;
+			break;
+		case 's':
+		case 'S':
+			string = va_arg(ap, byte *);
+			length += string[0] + 1;
+			for (j = 0; j <= string[0]; j++)
+				*write++ = string[j];
+			break;
+		}
+	}
+	va_end(ap);
+
+	PUT_WORD(&msg.header.length, length);
+	msg.header.controller = UnMapController(msg.header.controller);
+
+	if (command == _DATA_B3_I)
+		dlength = GET_WORD(
+			      ((byte *) & msg.info.data_b3_ind.Data_Length));
+
+	if (!(dmb = diva_os_alloc_message_buffer(length + dlength,
+					  (void **) &write))) {
+		DBG_ERR(("sendf: alloc_message_buffer failed, incoming msg dropped."))
+		return;
+	}
+
+	/* copy msg header to sk_buff */
+	memcpy(write, (byte *) & msg, length);
+
+	/* if DATA_B3_IND, copy data too */
+	if (command == _DATA_B3_I) {
+		dword data = GET_DWORD(&msg.info.data_b3_ind.Data);
+		memcpy(write + length, (void *) data, dlength);
+	}
+
+#ifndef DIVA_NO_DEBUGLIB
+	if (myDriverDebugHandle.dbgMask & DL_XLOG) {
+		switch (command) {
+		default:
+			xlog("\x00\x02", &msg, 0x81, length);
+			break;
+		case _DATA_B3_R | CONFIRM:
+			if (myDriverDebugHandle.dbgMask & DL_BLK)
+				xlog("\x00\x02", &msg, 0x81, length);
+			break;
+		case _DATA_B3_I:
+			if (myDriverDebugHandle.dbgMask & DL_BLK) {
+				xlog("\x00\x02", &msg, 0x81, length);
+				for (i = 0; i < dlength; i += 256) {
+				  DBG_BLK((((char *) GET_DWORD(&msg.info.data_b3_ind.Data)) + i,
+				  	((dlength - i) < 256) ? (dlength - i) : 256))
+				  if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
+					  break; /* not more if not explicitely requested */
+				}
+			}
+			break;
+		}
+	}
+#endif
+
+	/* find the card structure for this controller */
+	if (!(card = find_card_by_ctrl(write[8] & 0x7f))) {
+		DBG_ERR(("sendf - controller %d not found, incoming msg dropped",
+			 write[8] & 0x7f))
+		diva_os_free_message_buffer(dmb);
+		return;
+	}
+	/* send capi msg to capi layer */
+	capi_ctr_handle_message(&card->capi_ctrl, appl->Id, dmb);
+}
+
+/*
+ * cleanup adapter
+ */
+static void clean_adapter(int id, struct list_head *free_mem_q)
+{
+	DIVA_CAPI_ADAPTER *a;
+	int i, k;
+
+	a = &adapter[id];
+	k = li_total_channels - a->li_channels;
+	if (k == 0) {
+		if (li_config_table) {
+			list_add((struct list_head *)li_config_table, free_mem_q);
+			li_config_table = NULL;
+		}
+	} else {
+		if (a->li_base < k) {
+			memmove(&li_config_table[a->li_base],
+				&li_config_table[a->li_base + a->li_channels],
+				(k - a->li_base) * sizeof(LI_CONFIG));
+			for (i = 0; i < k; i++) {
+				memmove(&li_config_table[i].flag_table[a->li_base],
+					&li_config_table[i].flag_table[a->li_base + a->li_channels],
+					k - a->li_base);
+				memmove(&li_config_table[i].
+					coef_table[a->li_base],
+					&li_config_table[i].coef_table[a->li_base + a->li_channels],
+					k - a->li_base);
+			}
+		}
+	}
+	li_total_channels = k;
+	for (i = id; i < max_adapter; i++) {
+		if (adapter[i].request)
+			adapter[i].li_base -= a->li_channels;
+	}
+	if (a->plci)
+		list_add((struct list_head *)a->plci, free_mem_q);
+
+	memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER));
+	while ((max_adapter != 0) && !adapter[max_adapter - 1].request)
+		max_adapter--;
+}
+
+/*
+ * remove a card, but ensures consistent state of LI tables
+ * in the time adapter is removed
+ */
+static void divacapi_remove_card(DESCRIPTOR * d)
+{
+	diva_card *card = NULL;
+	diva_os_spin_lock_magic_t old_irql;
+	LIST_HEAD(free_mem_q);
+	struct list_head *link;
+	struct list_head *tmp;
+
+	/*
+	 * Set "remove in progress flag".
+	 * Ensures that there is no call from sendf to CAPI in
+	 * the time CAPI controller is about to be removed.
+	 */
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, diva_card, list);
+		if (card->d.request == d->request) {
+			card->remove_in_progress = 1;
+			list_del(tmp);
+			break;
+		}
+	}
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
+
+	if (card) {
+		/*
+		 * Detach CAPI. Sendf cannot call to CAPI any more.
+		 * After detach no call to send_message() is done too.
+		 */
+		detach_capi_ctr(&card->capi_ctrl);
+
+		/*
+		 * Now get API lock (to ensure stable state of LI tables)
+		 * and update the adapter map/LI table.
+		 */
+		diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
+
+		clean_adapter(card->Id - 1, &free_mem_q);
+		DBG_TRC(("DelAdapterMap (%d) -> (%d)",
+				ControllerMap[card->Id], card->Id))
+				ControllerMap[card->Id] = 0;
+		DBG_TRC(("adapter remove, max_adapter=%d",
+				max_adapter));
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
+		
+		/* After releasing the lock, we can free the memory */
+		diva_os_free (0, card);
+	}
+
+	/* free queued memory areas */
+	list_for_each_safe(link, tmp, &free_mem_q) {
+		list_del(link);
+		diva_os_free(0, link);
+	}
+}
+
+/*
+ * remove cards
+ */
+static void divacapi_remove_cards(void)
+{
+	DESCRIPTOR d;
+	struct list_head *tmp;
+	diva_card *card;
+	diva_os_spin_lock_magic_t old_irql;
+
+rescan:
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "remove cards");
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, diva_card, list);
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
+		d.request = card->d.request;
+		divacapi_remove_card(&d);
+		goto rescan;
+	}
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
+}
+
+/*
+ * sync_callback
+ */
+static void sync_callback(ENTITY * e)
+{
+	diva_os_spin_lock_magic_t old_irql;
+
+	DBG_TRC(("cb:Id=%x,Rc=%x,Ind=%x", e->Id, e->Rc, e->Ind))
+
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "sync_callback");
+	callback(e);
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "sync_callback");
+}
+
+/*
+ * add a new card
+ */
+static int diva_add_card(DESCRIPTOR * d)
+{
+	int k = 0, i = 0;
+	diva_os_spin_lock_magic_t old_irql;
+	diva_card *card = NULL;
+	struct capi_ctr *ctrl = NULL;
+	DIVA_CAPI_ADAPTER *a = NULL;
+	IDI_SYNC_REQ sync_req;
+	char serial[16];
+	void* mem_to_free;
+	LI_CONFIG *new_li_config_table;
+	int j;
+
+	if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) {
+		DBG_ERR(("diva_add_card: failed to allocate card struct."))
+		    return (0);
+	}
+	memset((char *) card, 0x00, sizeof(diva_card));
+	memcpy(&card->d, d, sizeof(DESCRIPTOR));
+	sync_req.GetName.Req = 0;
+	sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
+	card->d.request((ENTITY *) & sync_req);
+	strlcpy(card->name, sync_req.GetName.name, sizeof(card->name));
+	ctrl = &card->capi_ctrl;
+	strcpy(ctrl->name, card->name);
+	ctrl->register_appl = diva_register_appl;
+	ctrl->release_appl = diva_release_appl;
+	ctrl->send_message = diva_send_message;
+	ctrl->procinfo = diva_procinfo;
+	ctrl->driverdata = card;
+	diva_os_set_controller_struct(ctrl);
+
+	if (attach_capi_ctr(ctrl)) {
+		DBG_ERR(("diva_add_card: failed to attach controller."))
+		    diva_os_free(0, card);
+		return (0);
+	}
+	
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "find id");
+	card->Id = find_free_id();
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "find id");
+	
+	strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu));
+	ctrl->version.majorversion = 2;
+	ctrl->version.minorversion = 0;
+	ctrl->version.majormanuversion = DRRELMAJOR;
+	ctrl->version.minormanuversion = DRRELMINOR;
+	sync_req.GetSerial.Req = 0;
+	sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+	sync_req.GetSerial.serial = 0;
+	card->d.request((ENTITY *) & sync_req);
+	if ((i = ((sync_req.GetSerial.serial & 0xff000000) >> 24))) {
+		sprintf(serial, "%ld-%d",
+			sync_req.GetSerial.serial & 0x00ffffff, i + 1);
+	} else {
+		sprintf(serial, "%ld", sync_req.GetSerial.serial);
+	}
+	serial[CAPI_SERIAL_LEN - 1] = 0;
+	strlcpy(ctrl->serial, serial, sizeof(ctrl->serial));
+
+	a = &adapter[card->Id - 1];
+	card->adapter = a;
+	a->os_card = card;
+	ControllerMap[card->Id] = (byte) (ctrl->cnr);
+
+	DBG_TRC(("AddAdapterMap (%d) -> (%d)", ctrl->cnr, card->Id))
+
+	    sync_req.xdi_capi_prms.Req = 0;
+	sync_req.xdi_capi_prms.Rc = IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS;
+	sync_req.xdi_capi_prms.info.structure_length =
+	    sizeof(diva_xdi_get_capi_parameters_t);
+	card->d.request((ENTITY *) & sync_req);
+	a->flag_dynamic_l1_down =
+	    sync_req.xdi_capi_prms.info.flag_dynamic_l1_down;
+	a->group_optimization_enabled =
+	    sync_req.xdi_capi_prms.info.group_optimization_enabled;
+	a->request = DIRequest;	/* card->d.request; */
+	a->max_plci = card->d.channels + 30;
+	a->max_listen = (card->d.channels > 2) ? 8 : 2;
+	if (!
+	    (a->plci =
+	     (PLCI *) diva_os_malloc(0, sizeof(PLCI) * a->max_plci))) {
+		DBG_ERR(("diva_add_card: failed alloc plci struct."))
+		    memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
+		return (0);
+	}
+	memset(a->plci, 0, sizeof(PLCI) * a->max_plci);
+
+	for (k = 0; k < a->max_plci; k++) {
+		a->Id = (byte) card->Id;
+		a->plci[k].Sig.callback = sync_callback;
+		a->plci[k].Sig.XNum = 1;
+		a->plci[k].Sig.X = a->plci[k].XData;
+		a->plci[k].Sig.user[0] = (word) (card->Id - 1);
+		a->plci[k].Sig.user[1] = (word) k;
+		a->plci[k].NL.callback = sync_callback;
+		a->plci[k].NL.XNum = 1;
+		a->plci[k].NL.X = a->plci[k].XData;
+		a->plci[k].NL.user[0] = (word) ((card->Id - 1) | 0x8000);
+		a->plci[k].NL.user[1] = (word) k;
+		a->plci[k].adapter = a;
+	}
+
+	a->profile.Number = card->Id;
+	a->profile.Channels = card->d.channels;
+	if (card->d.features & DI_FAX3) {
+		a->profile.Global_Options = 0x71;
+		if (card->d.features & DI_CODEC)
+			a->profile.Global_Options |= 0x6;
+#if IMPLEMENT_DTMF
+		a->profile.Global_Options |= 0x8;
+#endif				/* IMPLEMENT_DTMF */
+		a->profile.Global_Options |= 0x80; /* Line Interconnect */
+#if IMPLEMENT_ECHO_CANCELLER
+		a->profile.Global_Options |= 0x100;
+#endif				/* IMPLEMENT_ECHO_CANCELLER */
+		a->profile.B1_Protocols = 0xdf;
+		a->profile.B2_Protocols = 0x1fdb;
+		a->profile.B3_Protocols = 0xb7;
+		a->manufacturer_features = MANUFACTURER_FEATURE_HARDDTMF;
+	} else {
+		a->profile.Global_Options = 0x71;
+		if (card->d.features & DI_CODEC)
+			a->profile.Global_Options |= 0x2;
+		a->profile.B1_Protocols = 0x43;
+		a->profile.B2_Protocols = 0x1f0f;
+		a->profile.B3_Protocols = 0x07;
+		a->manufacturer_features = 0;
+	}
+
+	a->li_pri = (a->profile.Channels > 2);
+	a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI;
+	a->li_base = 0;
+	for (i = 0; &adapter[i] != a; i++) {
+		if (adapter[i].request)
+			a->li_base = adapter[i].li_base + adapter[i].li_channels;
+	}
+	k = li_total_channels + a->li_channels;
+	new_li_config_table =
+		(LI_CONFIG *) diva_os_malloc(0, ((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * k) * ((k + 3) & ~3));
+	if (new_li_config_table == NULL) {
+		DBG_ERR(("diva_add_card: failed alloc li_config table."))
+		memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
+		return (0);
+	}
+
+	/* Prevent access to line interconnect table in process update */
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "add card");
+	
+	j = 0;
+	for (i = 0; i < k; i++) {
+		if ((i >= a->li_base) && (i < a->li_base + a->li_channels))
+			memset(&new_li_config_table[i], 0, sizeof(LI_CONFIG));
+		else
+			memcpy(&new_li_config_table[i], &li_config_table[j], sizeof(LI_CONFIG));
+		new_li_config_table[i].flag_table =
+			((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i) * ((k + 3) & ~3));
+		new_li_config_table[i].coef_table =
+			((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i + 1) * ((k + 3) & ~3));
+		if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) {
+			new_li_config_table[i].adapter = a;
+			memset(&new_li_config_table[i].flag_table[0], 0, k);
+			memset(&new_li_config_table[i].coef_table[0], 0, k);
+		} else {
+			if (a->li_base != 0) {
+				memcpy(&new_li_config_table[i].flag_table[0],
+				       &li_config_table[j].flag_table[0],
+				       a->li_base);
+				memcpy(&new_li_config_table[i].coef_table[0],
+				       &li_config_table[j].coef_table[0],
+				       a->li_base);
+			}
+			memset(&new_li_config_table[i].flag_table[a->li_base], 0, a->li_channels);
+			memset(&new_li_config_table[i].coef_table[a->li_base], 0, a->li_channels);
+			if (a->li_base + a->li_channels < k) {
+				memcpy(&new_li_config_table[i].flag_table[a->li_base +
+				       a->li_channels],
+				       &li_config_table[j].flag_table[a->li_base],
+				       k - (a->li_base + a->li_channels));
+				memcpy(&new_li_config_table[i].coef_table[a->li_base +
+				       a->li_channels],
+				       &li_config_table[j].coef_table[a->li_base],
+				       k - (a->li_base + a->li_channels));
+			}
+			j++;
+		}
+	}
+	li_total_channels = k;
+
+	mem_to_free = li_config_table;
+
+	li_config_table = new_li_config_table;
+	for (i = card->Id; i < max_adapter; i++) {
+		if (adapter[i].request)
+			adapter[i].li_base += a->li_channels;
+	}
+
+	if (a == &adapter[max_adapter])
+		max_adapter++;
+
+	list_add(&(card->list), &cards);
+	AutomaticLaw(a);
+
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "add card");
+
+	if (mem_to_free) {
+		diva_os_free (0, mem_to_free);
+	}
+
+	i = 0;
+	while (i++ < 30) {
+		if (a->automatic_law > 3)
+			break;
+		diva_os_sleep(10);
+	}
+
+	/* profile information */
+	PUT_WORD(&ctrl->profile.nbchannel, card->d.channels);
+	ctrl->profile.goptions = a->profile.Global_Options;
+	ctrl->profile.support1 = a->profile.B1_Protocols;
+	ctrl->profile.support2 = a->profile.B2_Protocols;
+	ctrl->profile.support3 = a->profile.B3_Protocols;
+	/* manufacturer profile information */
+	ctrl->profile.manu[0] = a->man_profile.private_options;
+	ctrl->profile.manu[1] = a->man_profile.rtp_primary_payloads;
+	ctrl->profile.manu[2] = a->man_profile.rtp_additional_payloads;
+	ctrl->profile.manu[3] = 0;
+	ctrl->profile.manu[4] = 0;
+
+	capi_ctr_ready(ctrl);
+
+	DBG_TRC(("adapter added, max_adapter=%d", max_adapter));
+	return (1);
+}
+
+/*
+ *  register appl
+ */
+static void diva_register_appl(struct capi_ctr *ctrl, __u16 appl,
+			       capi_register_params * rp)
+{
+	APPL *this;
+	word bnum, xnum;
+	int i = 0;
+	unsigned char *p;
+	void *DataNCCI, *DataFlags, *ReceiveBuffer, *xbuffer_used;
+	void **xbuffer_ptr, **xbuffer_internal;
+	diva_os_spin_lock_magic_t old_irql;
+	unsigned int mem_len;
+	int nconn = rp->level3cnt;
+
+
+	if (diva_os_in_irq()) {
+		DBG_ERR(("CAPI_REGISTER - in irq context !"))
+		return;
+	}
+
+	DBG_TRC(("application register Id=%d", appl))
+
+	if (appl > MAX_APPL) {
+		DBG_ERR(("CAPI_REGISTER - appl.Id exceeds MAX_APPL"))
+		return;
+	}
+
+	if (nconn <= 0)
+		nconn = ctrl->profile.nbchannel * -nconn;
+
+        if (nconn == 0)
+		nconn = ctrl->profile.nbchannel;
+
+	DBG_LOG(("CAPI_REGISTER - Id = %d", appl))
+	DBG_LOG(("  MaxLogicalConnections = %d(%d)", nconn, rp->level3cnt))
+	DBG_LOG(("  MaxBDataBuffers       = %d", rp->datablkcnt))
+	DBG_LOG(("  MaxBDataLength        = %d", rp->datablklen))
+
+	if (nconn < 1 ||
+	    nconn > 255 ||
+	    rp->datablklen < 80 ||
+	    rp->datablklen > 2150 || rp->datablkcnt > 255) {
+		DBG_ERR(("CAPI_REGISTER - invalid parameters"))
+		return;
+	}
+
+	if (application[appl - 1].Id == appl) {
+		DBG_LOG(("CAPI_REGISTER - appl already registered"))
+		return;	/* appl already registered */
+	}
+
+	/* alloc memory */
+
+	bnum = nconn * rp->datablkcnt;
+	xnum = nconn * MAX_DATA_B3;
+
+	mem_len  = bnum * sizeof(word);		/* DataNCCI */
+	mem_len += bnum * sizeof(word);		/* DataFlags */
+	mem_len += bnum * rp->datablklen;	/* ReceiveBuffer */
+	mem_len += xnum;			/* xbuffer_used */
+	mem_len += xnum * sizeof(void *);	/* xbuffer_ptr */
+	mem_len += xnum * sizeof(void *);	/* xbuffer_internal */
+	mem_len += xnum * rp->datablklen;	/* xbuffer_ptr[xnum] */
+
+	DBG_LOG(("  Allocated Memory      = %d", mem_len))
+	if (!(p = diva_os_malloc(0, mem_len))) {
+		DBG_ERR(("CAPI_REGISTER - memory allocation failed"))
+		return;
+	}
+	memset(p, 0, mem_len);
+
+	DataNCCI = (void *)p;
+	p += bnum * sizeof(word);
+	DataFlags = (void *)p;
+	p += bnum * sizeof(word);
+	ReceiveBuffer = (void *)p;
+	p += bnum * rp->datablklen;
+	xbuffer_used = (void *)p;
+	p += xnum;
+	xbuffer_ptr = (void **)p;
+	p += xnum * sizeof(void *);
+	xbuffer_internal = (void **)p;
+	p += xnum * sizeof(void *);
+	for (i = 0; i < xnum; i++) {
+		xbuffer_ptr[i] = (void *)p;
+		p += rp->datablklen;
+	}
+
+	/* initialize application data */
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "register_appl");
+
+	this = &application[appl - 1];
+	memset(this, 0, sizeof(APPL));
+
+	this->Id = appl;
+
+	for (i = 0; i < max_adapter; i++) {
+		adapter[i].CIP_Mask[appl - 1] = 0;
+	}
+
+	this->queue_size = 1000;
+
+	this->MaxNCCI = (byte) nconn;
+	this->MaxNCCIData = (byte) rp->datablkcnt;
+	this->MaxBuffer = bnum;
+	this->MaxDataLength = rp->datablklen;
+
+	this->DataNCCI = DataNCCI;
+	this->DataFlags = DataFlags;
+	this->ReceiveBuffer = ReceiveBuffer;
+	this->xbuffer_used = xbuffer_used;
+	this->xbuffer_ptr = xbuffer_ptr;
+	this->xbuffer_internal = xbuffer_internal;
+	for (i = 0; i < xnum; i++) {
+		this->xbuffer_ptr[i] = xbuffer_ptr[i];
+	}
+
+	CapiRegister(this->Id);
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "register_appl");
+
+}
+
+/*
+ *  release appl
+ */
+static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	APPL *this = &application[appl - 1];
+	void *mem_to_free = NULL;
+
+	DBG_TRC(("application %d(%d) cleanup", this->Id, appl))
+
+	if (diva_os_in_irq()) {
+		DBG_ERR(("CAPI_RELEASE - in irq context !"))
+		return;
+	}
+
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl");
+	if (this->Id) {
+		CapiRelease(this->Id);
+		mem_to_free = this->DataNCCI;
+		this->DataNCCI = NULL;
+		this->Id = 0;
+	}
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl");
+
+	if (mem_to_free)
+		diva_os_free(0, mem_to_free);
+
+}
+
+/*
+ *  send message
+ */
+static u16 diva_send_message(struct capi_ctr *ctrl,
+			     diva_os_message_buffer_s * dmb)
+{
+	int i = 0;
+	word ret = 0;
+	diva_os_spin_lock_magic_t old_irql;
+	CAPI_MSG *msg = (CAPI_MSG *) DIVA_MESSAGE_BUFFER_DATA(dmb);
+	APPL *this = &application[GET_WORD(&msg->header.appl_id) - 1];
+	diva_card *card = ctrl->driverdata;
+	__u32 length = DIVA_MESSAGE_BUFFER_LEN(dmb);
+	word clength = GET_WORD(&msg->header.length);
+	word command = GET_WORD(&msg->header.command);
+	u16 retval = CAPI_NOERROR;
+
+	if (diva_os_in_irq()) {
+		DBG_ERR(("CAPI_SEND_MSG - in irq context !"))
+		return CAPI_REGOSRESOURCEERR;
+	}
+	DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command))
+
+	if (card->remove_in_progress) {
+		DBG_ERR(("CAPI_SEND_MSG - remove in progress!"))
+		return CAPI_REGOSRESOURCEERR;
+	}
+
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "send message");
+
+	if (!this->Id) {
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
+		return CAPI_ILLAPPNR;
+	}
+
+	/* patch controller number */
+	msg->header.controller = ControllerMap[card->Id]
+	    | (msg->header.controller & 0x80);	/* preserve external controller bit */
+
+	switch (command) {
+	default:
+		xlog("\x00\x02", msg, 0x80, clength);
+		break;
+
+	case _DATA_B3_I | RESPONSE:
+#ifndef DIVA_NO_DEBUGLIB
+		if (myDriverDebugHandle.dbgMask & DL_BLK)
+			xlog("\x00\x02", msg, 0x80, clength);
+#endif
+		break;
+
+	case _DATA_B3_R:
+#ifndef DIVA_NO_DEBUGLIB
+		if (myDriverDebugHandle.dbgMask & DL_BLK)
+			xlog("\x00\x02", msg, 0x80, clength);
+#endif
+
+		if (clength == 24)
+			clength = 22;	/* workaround for PPcom bug */
+		/* header is always 22      */
+		if (GET_WORD(&msg->info.data_b3_req.Data_Length) >
+		    this->MaxDataLength
+		    || GET_WORD(&msg->info.data_b3_req.Data_Length) >
+		    (length - clength)) {
+			DBG_ERR(("Write - invalid message size"))
+			retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+			goto write_end;
+		}
+
+		for (i = 0; i < (MAX_DATA_B3 * this->MaxNCCI)
+		     && this->xbuffer_used[i]; i++);
+		if (i == (MAX_DATA_B3 * this->MaxNCCI)) {
+			DBG_ERR(("Write - too many data pending"))
+			retval = CAPI_SENDQUEUEFULL;
+			goto write_end;
+		}
+		msg->info.data_b3_req.Data = i;
+
+		this->xbuffer_internal[i] = NULL;
+		memcpy(this->xbuffer_ptr[i], &((__u8 *) msg)[clength],
+		       GET_WORD(&msg->info.data_b3_req.Data_Length));
+
+#ifndef DIVA_NO_DEBUGLIB
+		if ((myDriverDebugHandle.dbgMask & DL_BLK)
+		    && (myDriverDebugHandle.dbgMask & DL_XLOG)) {
+			int j;
+			for (j = 0; j <
+			     GET_WORD(&msg->info.data_b3_req.Data_Length);
+			     j += 256) {
+				DBG_BLK((((char *) this->xbuffer_ptr[i]) + j,
+					((GET_WORD(&msg->info.data_b3_req.Data_Length) - j) <
+					  256) ? (GET_WORD(&msg->info.data_b3_req.Data_Length) - j) : 256))
+				if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
+					break;	/* not more if not explicitely requested */
+			}
+		}
+#endif
+		break;
+	}
+
+	memcpy(mapped_msg, msg, (__u32) clength);
+	mapped_msg->header.controller = MapController(mapped_msg->header.controller);
+	mapped_msg->header.length = clength;
+	mapped_msg->header.command = command;
+	mapped_msg->header.number = GET_WORD(&msg->header.number);
+
+	ret = api_put(this, mapped_msg);
+	switch (ret) {
+	case 0:
+		break;
+	case _BAD_MSG:
+		DBG_ERR(("Write - bad message"))
+		retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+		break;
+	case _QUEUE_FULL:
+		DBG_ERR(("Write - queue full"))
+		retval = CAPI_SENDQUEUEFULL;
+		break;
+	default:
+		DBG_ERR(("Write - api_put returned unknown error"))
+		retval = CAPI_UNKNOWNNOTPAR;
+		break;
+	}
+
+      write_end:
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
+	if (retval == CAPI_NOERROR)
+		diva_os_free_message_buffer(dmb);
+	return retval;
+}
+
+
+/*
+ * cards request function
+ */
+static void DIRequest(ENTITY * e)
+{
+	DIVA_CAPI_ADAPTER *a = &(adapter[(byte) e->user[0]]);
+	diva_card *os_card = (diva_card *) a->os_card;
+
+	if (e->Req && (a->FlowControlIdTable[e->ReqCh] == e->Id)) {
+		a->FlowControlSkipTable[e->ReqCh] = 1;
+	}
+
+	(*(os_card->d.request)) (e);
+}
+
+/*
+ * callback function from didd
+ */
+static void didd_callback(void *context, DESCRIPTOR * adapter, int removal)
+{
+	if (adapter->type == IDI_DADAPTER) {
+		DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+		return;
+	} else if (adapter->type == IDI_DIMAINT) {
+		if (removal) {
+			stop_dbg();
+		} else {
+			memcpy(&MAdapter, adapter, sizeof(MAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
+		}
+	} else if ((adapter->type > 0) && (adapter->type < 16)) {	/* IDI Adapter */
+		if (removal) {
+			divacapi_remove_card(adapter);
+		} else {
+			diva_add_card(adapter);
+		}
+	}
+	return;
+}
+
+/*
+ * connect to didd
+ */
+static int divacapi_connect_didd(void)
+{
+	int x = 0;
+	int dadapter = 0;
+	IDI_SYNC_REQ req;
+	DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+	DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DIMAINT) {	/* MAINT found */
+			memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
+			break;
+		}
+	}
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DADAPTER) {	/* DADAPTER found */
+			dadapter = 1;
+			memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			req.didd_notify.e.Req = 0;
+			req.didd_notify.e.Rc =
+			    IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+			req.didd_notify.info.callback = (void *)didd_callback;
+			req.didd_notify.info.context = NULL;
+			DAdapter.request((ENTITY *) & req);
+			if (req.didd_notify.e.Rc != 0xff) {
+				stop_dbg();
+				return (0);
+			}
+			notify_handle = req.didd_notify.info.handle;
+		}
+			else if ((DIDD_Table[x].type > 0) && (DIDD_Table[x].type < 16)) {	/* IDI Adapter found */
+			diva_add_card(&DIDD_Table[x]);
+		}
+	}
+
+	if (!dadapter) {
+		stop_dbg();
+	}
+
+	return (dadapter);
+}
+
+/*
+ * diconnect from didd
+ */
+static void divacapi_disconnect_didd(void)
+{
+	IDI_SYNC_REQ req;
+
+	stop_dbg();
+
+	req.didd_notify.e.Req = 0;
+	req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+	req.didd_notify.info.handle = notify_handle;
+	DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * we do not provide date/time here,
+ * the application should do this. 
+ */
+int fax_head_line_time(char *buffer)
+{
+	return (0);
+}
+
+/*
+ * init (alloc) main structures
+ */
+static int DIVA_INIT_FUNCTION init_main_structs(void)
+{
+	if (!(mapped_msg = (CAPI_MSG *) diva_os_malloc(0, MAX_MSG_SIZE))) {
+		DBG_ERR(("init: failed alloc mapped_msg."))
+		    return 0;
+	}
+
+	if (!(adapter = diva_os_malloc(0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS))) {
+		DBG_ERR(("init: failed alloc adapter struct."))
+		diva_os_free(0, mapped_msg);
+		return 0;
+	}
+	memset(adapter, 0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS);
+
+	if (!(application = diva_os_malloc(0, sizeof(APPL) * MAX_APPL))) {
+		DBG_ERR(("init: failed alloc application struct."))
+		diva_os_free(0, mapped_msg);
+		diva_os_free(0, adapter);
+		return 0;
+	}
+	memset(application, 0, sizeof(APPL) * MAX_APPL);
+
+	return (1);
+}
+
+/*
+ * remove (free) main structures
+ */
+static void remove_main_structs(void)
+{
+	if (application)
+		diva_os_free(0, application);
+	if (adapter)
+		diva_os_free(0, adapter);
+	if (mapped_msg)
+		diva_os_free(0, mapped_msg);
+}
+
+/*
+ * api_remove_start
+ */
+static void do_api_remove_start(void)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	int ret = 1, count = 100;
+
+	do {
+		diva_os_enter_spin_lock(&api_lock, &old_irql, "api remove start");
+		ret = api_remove_start();
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "api remove start");
+
+		diva_os_sleep(10);
+	} while (ret && count--);
+
+	if (ret)
+		DBG_ERR(("could not remove signaling ID's"))
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION init_capifunc(void)
+{
+	diva_os_initialize_spin_lock(&api_lock, "capifunc");
+	memset(ControllerMap, 0, MAX_DESCRIPTORS + 1);
+	max_adapter = 0;
+
+
+	if (!init_main_structs()) {
+		DBG_ERR(("init: failed to init main structs."))
+		diva_os_destroy_spin_lock(&api_lock, "capifunc");
+		return (0);
+	}
+
+	if (!divacapi_connect_didd()) {
+		DBG_ERR(("init: failed to connect to DIDD."))
+		do_api_remove_start();
+		divacapi_remove_cards();
+		remove_main_structs();
+		diva_os_destroy_spin_lock(&api_lock, "capifunc");
+		return (0);
+	}
+
+	return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION finit_capifunc(void)
+{
+	do_api_remove_start();
+	divacapi_disconnect_didd();
+	divacapi_remove_cards();
+	remove_main_structs();
+	diva_os_destroy_spin_lock(&api_lock, "capifunc");
+}