453 lines
9.8 KiB
C
453 lines
9.8 KiB
C
/* $Id$
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include "m_capi.h"
|
|
#include "helper.h"
|
|
#include "debug.h"
|
|
|
|
static char *capi_revision = "$Revision$";
|
|
|
|
static int debug = 0;
|
|
static mISDNobject_t capi_obj;
|
|
|
|
static char MName[] = "mISDN Capi 2.0";
|
|
|
|
#ifdef MODULE
|
|
MODULE_AUTHOR("Karsten Keil");
|
|
#ifdef MODULE_LICENSE
|
|
MODULE_LICENSE("GPL");
|
|
#endif
|
|
MODULE_PARM(debug, "1i");
|
|
#endif
|
|
|
|
static char deb_buf[256];
|
|
|
|
void capidebug(int level, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (debug & level) {
|
|
va_start(args, fmt);
|
|
vsprintf(deb_buf, fmt, args);
|
|
printk(KERN_DEBUG "%s\n", deb_buf);
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
#ifdef OLDCAPI_DRIVER_INTERFACE
|
|
struct capi_driver_interface *cdrv_if;
|
|
#endif
|
|
|
|
kmem_cache_t *mISDN_cmsg_cp;
|
|
kmem_cache_t *mISDN_AppPlci_cp;
|
|
kmem_cache_t *mISDN_ncci_cp;
|
|
kmem_cache_t *mISDN_sspc_cp;
|
|
|
|
#ifdef MISDN_KMEM_DEBUG
|
|
static struct list_head mISDN_kmem_garbage = LIST_HEAD_INIT(mISDN_kmem_garbage);
|
|
|
|
_cmsg *
|
|
_kd_cmsg_alloc(char *fn, int line)
|
|
{
|
|
_kd_cmsg_t *ki = kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC);
|
|
|
|
if (!ki)
|
|
return(NULL);
|
|
ki->kdi.typ = KM_DBG_TYP_CM;
|
|
INIT_LIST_HEAD(&ki->kdi.head);
|
|
ki->kdi.line = line;
|
|
ki->kdi.file = fn;
|
|
list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
|
|
return(&ki->cm);
|
|
}
|
|
|
|
void
|
|
cmsg_free(_cmsg *cm)
|
|
{
|
|
km_dbg_item_t *kdi;
|
|
|
|
if (!cm) {
|
|
int_errtxt("zero pointer free at %p", __builtin_return_address(0));
|
|
return;
|
|
}
|
|
kdi = KDB_GET_KDI(cm);
|
|
list_del(&kdi->head);
|
|
kmem_cache_free(mISDN_cmsg_cp, kdi);
|
|
}
|
|
|
|
AppPlci_t *
|
|
_kd_AppPlci_alloc(char *fn, int line)
|
|
{
|
|
_kd_AppPlci_t *ki = kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC);
|
|
|
|
if (!ki)
|
|
return(NULL);
|
|
ki->kdi.typ = KM_DBG_TYP_AP;
|
|
INIT_LIST_HEAD(&ki->kdi.head);
|
|
ki->kdi.line = line;
|
|
ki->kdi.file = fn;
|
|
list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
|
|
return(&ki->ap);
|
|
}
|
|
|
|
void
|
|
AppPlci_free(AppPlci_t *ap)
|
|
{
|
|
km_dbg_item_t *kdi;
|
|
|
|
if (!ap) {
|
|
int_errtxt("zero pointer free at %p", __builtin_return_address(0));
|
|
return;
|
|
}
|
|
kdi = KDB_GET_KDI(ap);
|
|
list_del(&kdi->head);
|
|
kmem_cache_free(mISDN_AppPlci_cp, kdi);
|
|
}
|
|
|
|
Ncci_t *
|
|
_kd_ncci_alloc(char *fn, int line)
|
|
{
|
|
_kd_Ncci_t *ki = kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC);
|
|
|
|
if (!ki)
|
|
return(NULL);
|
|
ki->kdi.typ = KM_DBG_TYP_NI;
|
|
INIT_LIST_HEAD(&ki->kdi.head);
|
|
ki->kdi.line = line;
|
|
ki->kdi.file = fn;
|
|
list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
|
|
return(&ki->ni);
|
|
}
|
|
|
|
void
|
|
ncci_free(Ncci_t *ni)
|
|
{
|
|
km_dbg_item_t *kdi;
|
|
|
|
if (!ni) {
|
|
int_errtxt("zero pointer free at %p", __builtin_return_address(0));
|
|
return;
|
|
}
|
|
kdi = KDB_GET_KDI(ni);
|
|
list_del(&kdi->head);
|
|
kmem_cache_free(mISDN_ncci_cp, kdi);
|
|
}
|
|
|
|
SSProcess_t *
|
|
_kd_SSProcess_alloc(char *fn, int line)
|
|
{
|
|
_kd_SSProcess_t *ki = kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC);
|
|
|
|
if (!ki)
|
|
return(NULL);
|
|
ki->kdi.typ = KM_DBG_TYP_SP;
|
|
INIT_LIST_HEAD(&ki->kdi.head);
|
|
ki->kdi.line = line;
|
|
ki->kdi.file = fn;
|
|
list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
|
|
return(&ki->sp);
|
|
}
|
|
|
|
void
|
|
SSProcess_free(SSProcess_t *sp)
|
|
{
|
|
km_dbg_item_t *kdi;
|
|
|
|
if (!sp) {
|
|
int_errtxt("zero pointer free at %p", __builtin_return_address(0));
|
|
return;
|
|
}
|
|
kdi = KDB_GET_KDI(sp);
|
|
list_del(&kdi->head);
|
|
kmem_cache_free(mISDN_sspc_cp, kdi);
|
|
}
|
|
|
|
static void
|
|
free_garbage(void)
|
|
{
|
|
struct list_head *item, *next;
|
|
_kd_all_t *kda;
|
|
|
|
list_for_each_safe(item, next, &mISDN_kmem_garbage) {
|
|
kda = (_kd_all_t *)item;
|
|
printk(KERN_DEBUG "garbage item found (%p <- %p -> %p) type%ld allocated at %s:%d\n",
|
|
kda->kdi.head.prev, item, kda->kdi.head.next, kda->kdi.typ, kda->kdi.file, kda->kdi.line);
|
|
list_del(item);
|
|
switch(kda->kdi.typ) {
|
|
case KM_DBG_TYP_CM:
|
|
printk(KERN_DEBUG "cmsg cmd(%x,%x) appl(%x) addr(%x) nr(%d)\n",
|
|
kda->a.cm.Command,
|
|
kda->a.cm.Subcommand,
|
|
kda->a.cm.ApplId,
|
|
kda->a.cm.adr.adrController,
|
|
kda->a.cm.Messagenumber);
|
|
kmem_cache_free(mISDN_cmsg_cp, item);
|
|
break;
|
|
case KM_DBG_TYP_AP:
|
|
printk(KERN_DEBUG "AppPlci: PLCI(%x) m.state(%x) appl(%p)\n",
|
|
kda->a.ap.addr,
|
|
kda->a.ap.plci_m.state,
|
|
kda->a.ap.appl);
|
|
kmem_cache_free(mISDN_AppPlci_cp, item);
|
|
break;
|
|
case KM_DBG_TYP_NI:
|
|
printk(KERN_DEBUG "Ncci: NCCI(%x) state(%lx) m.state(%x) aplci(%p)\n",
|
|
kda->a.ni.addr,
|
|
kda->a.ni.state,
|
|
kda->a.ni.ncci_m.state,
|
|
kda->a.ni.AppPlci);
|
|
kmem_cache_free(mISDN_ncci_cp, item);
|
|
break;
|
|
case KM_DBG_TYP_SP:
|
|
printk(KERN_DEBUG "SSPc: addr(%x) id(%x) apid(%x) func(%x)\n",
|
|
kda->a.sp.addr,
|
|
kda->a.sp.invokeId,
|
|
kda->a.sp.ApplId,
|
|
kda->a.sp.Function);
|
|
kmem_cache_free(mISDN_sspc_cp, item);
|
|
break;
|
|
default:
|
|
printk(KERN_DEBUG "unknown garbage item(%p) type %ld\n",
|
|
item, kda->kdi.typ);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void CapiCachesFree(void)
|
|
{
|
|
#ifdef MISDN_KMEM_DEBUG
|
|
free_garbage();
|
|
#endif
|
|
if (mISDN_cmsg_cp) {
|
|
kmem_cache_destroy(mISDN_cmsg_cp);
|
|
mISDN_cmsg_cp = NULL;
|
|
}
|
|
if (mISDN_AppPlci_cp) {
|
|
kmem_cache_destroy(mISDN_AppPlci_cp);
|
|
mISDN_AppPlci_cp = NULL;
|
|
}
|
|
if (mISDN_ncci_cp) {
|
|
kmem_cache_destroy(mISDN_ncci_cp);
|
|
mISDN_ncci_cp = NULL;
|
|
}
|
|
if (mISDN_sspc_cp) {
|
|
kmem_cache_destroy(mISDN_sspc_cp);
|
|
mISDN_sspc_cp = NULL;
|
|
}
|
|
}
|
|
|
|
static int CapiNew(void)
|
|
{
|
|
mISDN_cmsg_cp = NULL;
|
|
mISDN_AppPlci_cp = NULL;
|
|
mISDN_ncci_cp = NULL;
|
|
mISDN_sspc_cp = NULL;
|
|
mISDN_cmsg_cp = kmem_cache_create("mISDN_cmesg",
|
|
#ifdef MISDN_KMEM_DEBUG
|
|
sizeof(_kd_cmsg_t),
|
|
#else
|
|
sizeof(_cmsg),
|
|
#endif
|
|
0, 0, NULL, NULL);
|
|
if (!mISDN_cmsg_cp) {
|
|
CapiCachesFree();
|
|
return(-ENOMEM);
|
|
}
|
|
mISDN_AppPlci_cp = kmem_cache_create("mISDN_AppPlci",
|
|
#ifdef MISDN_KMEM_DEBUG
|
|
sizeof(_kd_AppPlci_t),
|
|
#else
|
|
sizeof(AppPlci_t),
|
|
#endif
|
|
0, 0, NULL, NULL);
|
|
if (!mISDN_AppPlci_cp) {
|
|
CapiCachesFree();
|
|
return(-ENOMEM);
|
|
}
|
|
mISDN_ncci_cp = kmem_cache_create("mISDN_Ncci",
|
|
#ifdef MISDN_KMEM_DEBUG
|
|
sizeof(_kd_Ncci_t),
|
|
#else
|
|
sizeof(Ncci_t),
|
|
#endif
|
|
0, 0, NULL, NULL);
|
|
if (!mISDN_ncci_cp) {
|
|
CapiCachesFree();
|
|
return(-ENOMEM);
|
|
}
|
|
mISDN_sspc_cp = kmem_cache_create("mISDN_SSProc",
|
|
#ifdef MISDN_KMEM_DEBUG
|
|
sizeof(_kd_SSProcess_t),
|
|
#else
|
|
sizeof(SSProcess_t),
|
|
#endif
|
|
0, 0, NULL, NULL);
|
|
if (!mISDN_sspc_cp) {
|
|
CapiCachesFree();
|
|
return(-ENOMEM);
|
|
}
|
|
#ifdef OLDCAPI_DRIVER_INTERFACE
|
|
cdrv_if = attach_capi_driver(&mISDN_driver);
|
|
if (!cdrv_if) {
|
|
CapiCachesFree();
|
|
printk(KERN_ERR "mISDN: failed to attach capi_driver\n");
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
init_listen();
|
|
init_AppPlci();
|
|
init_ncci();
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
capi20_manager(void *data, u_int prim, void *arg) {
|
|
mISDNinstance_t *inst = data;
|
|
int found=0;
|
|
PLInst_t *plink = NULL;
|
|
Controller_t *ctrl;
|
|
u_long flags;
|
|
|
|
if (CAPI_DBG_INFO & debug)
|
|
printk(KERN_DEBUG "capi20_manager data:%p prim:%x arg:%p\n", data, prim, arg);
|
|
if (!data)
|
|
return(-EINVAL);
|
|
spin_lock_irqsave(&capi_obj.lock, flags);
|
|
list_for_each_entry(ctrl, &capi_obj.ilist, list) {
|
|
if (&ctrl->inst == inst) {
|
|
found++;
|
|
break;
|
|
}
|
|
list_for_each_entry(plink, &ctrl->linklist, list) {
|
|
if (&plink->inst == inst) {
|
|
found++;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
break;
|
|
plink = NULL;
|
|
}
|
|
if (&ctrl->list == &capi_obj.ilist)
|
|
ctrl = NULL;
|
|
spin_unlock_irqrestore(&capi_obj.lock, flags);
|
|
if (prim == (MGR_NEWLAYER | REQUEST)) {
|
|
int ret = ControllerConstr(&ctrl, data, arg, &capi_obj);
|
|
if (!ret)
|
|
ctrl->debug = debug;
|
|
return(ret);
|
|
}
|
|
if (!ctrl) {
|
|
if (CAPI_DBG_WARN & debug)
|
|
printk(KERN_WARNING "capi20_manager setif no instance\n");
|
|
return(-EINVAL);
|
|
}
|
|
switch(prim) {
|
|
case MGR_NEWENTITY | CONFIRM:
|
|
ctrl->entity = (u_long)arg & 0xffffffff;
|
|
break;
|
|
#ifdef FIXME
|
|
case MGR_CONNECT | REQUEST:
|
|
return(mISDN_ConnectIF(inst, arg));
|
|
case MGR_SETIF | INDICATION:
|
|
case MGR_SETIF | REQUEST:
|
|
if (&ctrl->inst == inst)
|
|
return(mISDN_SetIF(inst, arg, prim, NULL, ControllerL3L4, ctrl));
|
|
else
|
|
return(AppPlcimISDN_SetIF(inst->data, prim, arg));
|
|
case MGR_DISCONNECT | REQUEST:
|
|
case MGR_DISCONNECT | INDICATION:
|
|
return(mISDN_DisConnectIF(inst, arg));
|
|
#endif
|
|
case MGR_SETSTACK | INDICATION:
|
|
if (!(&ctrl->inst == inst))
|
|
return(AppPlcimISDN_Active(inst->privat));
|
|
return(0);
|
|
case MGR_RELEASE | INDICATION:
|
|
if (CAPI_DBG_INFO & debug)
|
|
printk(KERN_DEBUG "release_capi20 id %x\n", ctrl->inst.st->id);
|
|
ControllerDestr(ctrl);
|
|
break;
|
|
case MGR_UNREGLAYER | REQUEST:
|
|
if (plink) {
|
|
plink->inst.function = NULL;
|
|
capi_obj.ctrl(&plink->inst, MGR_UNREGLAYER | REQUEST, NULL);
|
|
}
|
|
break;
|
|
case MGR_CTRLREADY | INDICATION:
|
|
if (CAPI_DBG_INFO & debug)
|
|
printk(KERN_DEBUG "ctrl %x ready\n", ctrl->inst.st->id);
|
|
ControllerRun(ctrl);
|
|
break;
|
|
default:
|
|
if (CAPI_DBG_WARN & debug)
|
|
printk(KERN_WARNING "capi20_manager prim %x not handled\n", prim);
|
|
return(-EINVAL);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int Capi20Init(void)
|
|
{
|
|
int err;
|
|
|
|
printk(KERN_INFO "%s driver file version %s\n", MName, mISDN_getrev(capi_revision));
|
|
#ifdef MODULE
|
|
capi_obj.owner = THIS_MODULE;
|
|
#endif
|
|
capi_obj.name = MName;
|
|
capi_obj.DPROTO.protocol[4] = ISDN_PID_L4_CAPI20;
|
|
capi_obj.BPROTO.protocol[4] = ISDN_PID_L4_B_CAPI20;
|
|
capi_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_TRANS;
|
|
capi_obj.own_ctrl = capi20_manager;
|
|
spin_lock_init(&capi_obj.lock);
|
|
INIT_LIST_HEAD(&capi_obj.ilist);
|
|
if ((err = CapiNew()))
|
|
return(err);
|
|
if ((err = mISDN_register(&capi_obj))) {
|
|
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
|
|
#ifdef OLDCAPI_DRIVER_INTERFACE
|
|
detach_capi_driver(&mISDN_driver);
|
|
#endif
|
|
CapiCachesFree();
|
|
free_listen();
|
|
free_AppPlci();
|
|
free_ncci();
|
|
free_Application();
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
#ifdef MODULE
|
|
static void Capi20cleanup(void)
|
|
{
|
|
int err;
|
|
Controller_t *contr, *next;
|
|
|
|
if ((err = mISDN_unregister(&capi_obj))) {
|
|
printk(KERN_ERR "Can't unregister CAPI20 error(%d)\n", err);
|
|
}
|
|
if (!list_empty(&capi_obj.ilist)) {
|
|
printk(KERN_WARNING "mISDN controller list not empty\n");
|
|
list_for_each_entry_safe(contr, next, &capi_obj.ilist, list)
|
|
ControllerDestr(contr);
|
|
}
|
|
#ifdef OLDCAPI_DRIVER_INTERFACE
|
|
detach_capi_driver(&mISDN_driver);
|
|
#endif
|
|
free_Application();
|
|
CapiCachesFree();
|
|
free_listen();
|
|
free_AppPlci();
|
|
free_ncci();
|
|
}
|
|
|
|
module_init(Capi20Init);
|
|
module_exit(Capi20cleanup);
|
|
#endif
|