mirror of https://gitee.com/openkylin/linux.git
1220 lines
31 KiB
C
1220 lines
31 KiB
C
/* $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");
|
|
}
|