| /* $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"); | |
| } |