summaryrefslogtreecommitdiffstats
path: root/drivers/isdn/hardware/eicon/capifunc.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
commit76cb841cb886eef6b3bee341a2266c76578724ad (patch)
treef5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/isdn/hardware/eicon/capifunc.c
parentInitial commit. (diff)
downloadlinux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz
linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip
Adding upstream version 4.19.249.upstream/4.19.249upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/isdn/hardware/eicon/capifunc.c')
-rw-r--r--drivers/isdn/hardware/eicon/capifunc.c1219
1 files changed, 1219 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/eicon/capifunc.c b/drivers/isdn/hardware/eicon/capifunc.c
new file mode 100644
index 000000000..7a0bdbdd8
--- /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 *)(long)ref;
+}
+
+void *TransmitBufferGet(APPL *appl, void *p)
+{
+ if (appl->xbuffer_internal[(dword)(long)p])
+ return appl->xbuffer_internal[(dword)(long)p];
+
+ return appl->xbuffer_ptr[(dword)(long)p];
+}
+
+void TransmitBufferFree(APPL *appl, void *p)
+{
+ appl->xbuffer_used[(dword)(long)p] = false;
+ DBG_PRV1(("%d:xbuf_free(%d)", appl->Id, ((dword)(long)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 *)(long)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 *)(long)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 explicitly 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 explicitly 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 __init 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 __init 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 __exit finit_capifunc(void)
+{
+ do_api_remove_start();
+ divacapi_disconnect_didd();
+ divacapi_remove_cards();
+ remove_main_structs();
+ diva_os_destroy_spin_lock(&api_lock, "capifunc");
+}