diff options
Diffstat (limited to 'kernel/drivers/isdn/hardware/eicon/capifunc.c')
-rw-r--r-- | kernel/drivers/isdn/hardware/eicon/capifunc.c | 1219 |
1 files changed, 1219 insertions, 0 deletions
diff --git a/kernel/drivers/isdn/hardware/eicon/capifunc.c b/kernel/drivers/isdn/hardware/eicon/capifunc.c new file mode 100644 index 000000000..7a0bdbdd8 --- /dev/null +++ b/kernel/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"); +} |