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