mISDN/drivers/isdn/hardware/mISDN/contr.c

451 lines
9.3 KiB
C
Raw Normal View History

2001-02-21 19:22:35 +00:00
/* $Id$
*
*/
#include "hisax_capi.h"
#include "helper.h"
#include "debug.h"
#define contrDebug(contr, lev, fmt, args...) \
2001-02-22 05:54:40 +00:00
capidebug(lev, fmt, ## args)
2001-02-21 19:22:35 +00:00
2001-03-26 11:40:02 +00:00
int contrConstr(Contr_t *contr, hisaxstack_t *st, hisax_pid_t *pid, hisaxobject_t *ocapi)
2001-02-21 19:22:35 +00:00
{
2001-11-14 10:41:26 +00:00
char tmp[10];
hisaxstack_t *cst = st->child;
BInst_t *binst;
2001-02-21 19:22:35 +00:00
memset(contr, 0, sizeof(Contr_t));
2001-03-26 11:40:02 +00:00
memcpy(&contr->inst.pid, pid, sizeof(hisax_pid_t));
2001-02-21 19:22:35 +00:00
contr->adrController = st->id;
2001-03-11 21:23:39 +00:00
sprintf(contr->inst.name, "CAPI %d", st->id);
2001-02-22 05:54:40 +00:00
contr->inst.obj = ocapi;
2001-03-26 11:40:02 +00:00
if (!SetHandledPID(ocapi, &contr->inst.pid)) {
int_error();
return(-ENOPROTOOPT);
}
2001-02-22 05:54:40 +00:00
while(cst) {
if (!(binst = kmalloc(sizeof(BInst_t), GFP_ATOMIC))) {
2001-02-27 17:45:44 +00:00
printk(KERN_ERR "no mem for Binst\n");
2001-02-22 05:54:40 +00:00
int_error();
return -ENOMEM;
}
2001-02-27 17:45:44 +00:00
memset(binst, 0, sizeof(BInst_t));
2001-10-31 23:04:42 +00:00
binst->bst = cst;
2001-02-27 17:45:44 +00:00
binst->inst.st = cst;
binst->inst.data = binst;
binst->inst.obj = ocapi;
2001-03-26 11:40:02 +00:00
binst->inst.pid.layermask |= ISDN_LAYER(4);
2001-02-27 17:45:44 +00:00
binst->inst.down.stat = IF_NOACTIV;
APPEND_TO_LIST(binst, contr->binst);
2001-02-22 05:54:40 +00:00
cst = cst->next;
}
2001-03-26 11:40:02 +00:00
APPEND_TO_LIST(contr, ocapi->ilist);
2001-12-02 13:08:08 +00:00
sprintf(tmp, "HiSax%d", st->id);
2001-02-21 19:22:35 +00:00
contr->ctrl = cdrv_if->attach_ctr(&hisax_driver, tmp, contr);
if (!contr->ctrl)
return -ENODEV;
2001-12-02 13:08:08 +00:00
contr->adrController = contr->ctrl->cnr;
2001-02-21 19:22:35 +00:00
contr->inst.data = contr;
2001-03-26 11:40:02 +00:00
ocapi->ctrl(st, MGR_REGLAYER | INDICATION, &contr->inst);
2001-02-21 19:22:35 +00:00
contr->inst.up.stat = IF_DOWN;
return 0;
}
void contrDestr(Contr_t *contr)
{
int i;
2001-03-26 11:40:02 +00:00
hisaxinstance_t *inst = &contr->inst;
2001-02-21 19:22:35 +00:00
for (i = 0; i < CAPI_MAXAPPL; i++) {
if (contr->appls[i]) {
applDestr(contr->appls[i]);
kfree(contr->appls[i]);
2001-02-27 17:45:44 +00:00
contr->appls[i] = NULL;
2001-02-21 19:22:35 +00:00
}
}
for (i = 0; i < CAPI_MAXPLCI; i++) {
if (contr->plcis[i]) {
plciDestr(contr->plcis[i]);
kfree(contr->plcis[i]);
2001-02-27 17:45:44 +00:00
contr->plcis[i] = NULL;
2001-02-21 19:22:35 +00:00
}
}
for (i = 0; i < CAPI_MAXDUMMYPCS; i++) {
if (contr->dummy_pcs[i]) {
dummyPcDestr(contr->dummy_pcs[i]);
kfree(contr->dummy_pcs[i]);
2001-02-27 17:45:44 +00:00
contr->dummy_pcs[i] = NULL;
2001-02-21 19:22:35 +00:00
}
}
2001-02-22 05:54:40 +00:00
if (contr->ctrl)
cdrv_if->detach_ctr(contr->ctrl);
2001-02-27 17:45:44 +00:00
while (contr->binst) {
BInst_t *binst = contr->binst;
REMOVE_FROM_LISTBASE(binst, contr->binst);
kfree(binst);
2001-02-22 05:54:40 +00:00
}
2001-03-26 11:40:02 +00:00
if (inst->up.peer) {
inst->up.peer->obj->ctrl(inst->up.peer,
MGR_DISCONNECT | REQUEST, &inst->up);
}
if (inst->down.peer) {
inst->down.peer->obj->ctrl(inst->down.peer,
MGR_DISCONNECT | REQUEST, &inst->down);
}
inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
REMOVE_FROM_LISTBASE(contr, ((Contr_t *)inst->obj->ilist));
2001-02-21 19:22:35 +00:00
}
void contrRun(Contr_t *contr)
{
2001-11-14 10:41:26 +00:00
struct capi_ctr *ctrl = contr->ctrl;
BInst_t *binst;
int nb;
nb = 0;
binst = contr->binst;
while(binst) {
nb++;
binst = binst->next;
}
2001-02-21 19:22:35 +00:00
strncpy(ctrl->manu, "ISDN4Linux, (C) Kai Germaschewski", CAPI_MANUFACTURER_LEN);
strncpy(ctrl->serial, "0002", CAPI_SERIAL_LEN);
ctrl->version.majorversion = 2;
ctrl->version.minorversion = 0;
ctrl->version.majormanuversion = 1;
ctrl->version.minormanuversion = 1;
memset(&ctrl->profile, 0, sizeof(struct capi_profile));
ctrl->profile.ncontroller = 1;
2001-11-14 10:41:26 +00:00
ctrl->profile.nbchannel = nb;
2001-02-21 19:22:35 +00:00
ctrl->profile.goptions = 0x11; // internal controller, supplementary services
ctrl->profile.support1 = 3; // HDLC, TRANS
ctrl->profile.support2 = 3; // X75SLP, TRANS
ctrl->profile.support3 = 1; // TRANS
ctrl->ready(ctrl);
}
Appl_t *contrId2appl(Contr_t *contr, __u16 ApplId)
{
if ((ApplId < 1) || (ApplId > CAPI_MAXAPPL)) {
int_error();
return 0;
}
return contr->appls[ApplId - 1];
}
Plci_t *contrAdr2plci(Contr_t *contr, __u32 adr)
{
int i = (adr >> 8);
if ((i < 1) || (i > CAPI_MAXPLCI)) {
int_error();
return 0;
}
return contr->plcis[i - 1];
}
void contrRegisterAppl(Contr_t *contr, __u16 ApplId, capi_register_params *rp)
{
Appl_t *appl;
appl = contrId2appl(contr, ApplId);
if (appl) {
int_error();
return;
}
appl = kmalloc(sizeof(Appl_t), GFP_ATOMIC);
2001-02-21 19:22:35 +00:00
if (!appl) {
int_error();
return;
}
contr->appls[ApplId - 1] = appl;
applConstr(appl, contr, ApplId, rp);
contr->ctrl->appl_registered(contr->ctrl, ApplId);
}
void contrReleaseAppl(Contr_t *contr, __u16 ApplId)
{
Appl_t *appl;
printk(KERN_DEBUG "%s %x\n", __FUNCTION__, ApplId);
2001-02-21 19:22:35 +00:00
appl = contrId2appl(contr, ApplId);
if (!appl) {
int_error();
return;
}
applDestr(appl);
kfree(appl);
2001-02-27 17:45:44 +00:00
contr->appls[ApplId - 1] = NULL;
2001-02-21 19:22:35 +00:00
contr->ctrl->appl_released(contr->ctrl, ApplId);
}
void contrSendMessage(Contr_t *contr, struct sk_buff *skb)
{
Appl_t *appl;
int ApplId;
ApplId = CAPIMSG_APPID(skb->data);
appl = contrId2appl(contr, ApplId);
if (!appl) {
int_error();
return;
}
applSendMessage(appl, skb);
}
2001-02-27 17:45:44 +00:00
void contrLoadFirmware(Contr_t *contr, int len, void *data)
2001-02-21 19:22:35 +00:00
{
2001-03-03 18:17:16 +00:00
struct firm {
int len;
void *data;
} firm;
2001-02-27 17:45:44 +00:00
2001-03-03 18:17:16 +00:00
firm.len = len;
firm.data = data;
contr->inst.obj->ctrl(contr->inst.st, MGR_LOADFIRM | REQUEST, &firm);
2001-02-27 17:45:44 +00:00
contrRun(contr);
2001-02-21 19:22:35 +00:00
}
void contrReset(Contr_t *contr)
{
int ApplId;
Appl_t *appl;
for (ApplId = 1; ApplId <= CAPI_MAXAPPL; ApplId++) {
appl = contrId2appl(contr, ApplId);
if (appl)
applDestr(appl);
kfree(appl);
contr->appls[ApplId - 1] = NULL;
}
contr->ctrl->reseted(contr->ctrl);
}
void contrD2Trace(Contr_t *contr, u_char *buf, int len)
{
Appl_t *appl;
__u16 applId;
for (applId = 1; applId <= CAPI_MAXAPPL; applId++) {
appl = contrId2appl(contr, applId);
if (appl) {
applD2Trace(appl, buf, len);
}
}
}
void contrRecvCmsg(Contr_t *contr, _cmsg *cmsg)
{
struct sk_buff *skb;
int len;
capi_cmsg2message(cmsg, contr->msgbuf);
len = CAPIMSG_LEN(contr->msgbuf);
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
int_error();
return;
}
memcpy(skb_put(skb, len), contr->msgbuf, len);
contr->ctrl->handle_capimsg(contr->ctrl, cmsg->ApplId, skb);
}
void contrAnswerCmsg(Contr_t *contr, _cmsg *cmsg, __u16 Info)
{
capi_cmsg_answer(cmsg);
cmsg->Info = Info;
contrRecvCmsg(contr, cmsg);
}
void contrAnswerMessage(Contr_t *contr, struct sk_buff *skb, __u16 Info)
{
_cmsg cmsg;
capi_message2cmsg(&cmsg, skb->data);
contrAnswerCmsg(contr, &cmsg, Info);
}
Plci_t *contrNewPlci(Contr_t *contr)
{
Plci_t *plci;
int i;
for (i = 0; i < CAPI_MAXPLCI; i++) {
if (!contr->plcis[i])
break;
}
if (i == CAPI_MAXPLCI) {
return 0;
}
plci = kmalloc(sizeof(Plci_t), GFP_ATOMIC);
if (!plci) {
int_error();
return 0;
}
contr->plcis[i] = plci;
plciConstr(plci, contr, (i+1) << 8 | contr->adrController);
return plci;
}
void contrDelPlci(Contr_t *contr, Plci_t *plci)
{
int i = plci->adrPLCI >> 8;
2002-09-16 23:49:38 +00:00
contrDebug(contr, LL_DEB_INFO, "%s: PLCI(%x)", __FUNCTION__, plci->adrPLCI);
2001-02-21 19:22:35 +00:00
if ((i < 1) || (i > CAPI_MAXPLCI)) {
int_error();
return;
}
if (contr->plcis[i-1] != plci) {
int_error();
return;
}
plciDestr(plci);
kfree(plci);
2001-02-27 17:45:44 +00:00
contr->plcis[i-1] = NULL;
2001-02-21 19:22:35 +00:00
}
static Plci_t
*contrGetPLCI4addr(Contr_t *contr, int addr)
{
int i;
for (i = 0; i < CAPI_MAXPLCI; i++) {
if (!contr->plcis[i])
continue;
if (contr->plcis[i]->adrPLCI == addr)
return(contr->plcis[i]);
}
return(NULL);
}
2001-02-22 05:54:40 +00:00
int
2001-08-02 14:51:56 +00:00
contrL3L4(hisaxif_t *hif, struct sk_buff *skb)
2001-02-21 19:22:35 +00:00
{
2001-08-02 14:51:56 +00:00
Contr_t *contr;
Plci_t *plci;
__u32 *id;
int ret = -EINVAL;
hisax_head_t *hh;
if (!hif || !skb)
return(ret);
2002-05-01 01:00:40 +00:00
hh = HISAX_HEAD_P(skb);
2001-02-21 19:22:35 +00:00
contr = hif->fdata;
2001-08-02 14:51:56 +00:00
if (hh->prim == (CC_NEW_CR | INDICATION)) {
2001-02-21 19:22:35 +00:00
plci = contrNewPlci(contr);
if (!plci)
return(-EBUSY);
2001-08-02 14:51:56 +00:00
if (skb->len >= sizeof(void)) {
2001-10-31 23:04:42 +00:00
id = *((__u32 **)skb->data);
2001-08-02 14:51:56 +00:00
*id = plci->adrPLCI;
dev_kfree_skb(skb);
ret = 0;
}
} else if ((hh->dinfo & ~CONTROLER_MASK) == DUMMY_CR_FLAG) {
ret = contrDummyInd(contr, hh->prim, skb);
2001-02-21 19:22:35 +00:00
} else {
2001-08-02 14:51:56 +00:00
if (!(plci = contrGetPLCI4addr(contr, hh->dinfo))) {
2002-09-16 23:49:38 +00:00
contrDebug(contr, LL_DEB_WARN, ": unknown plci prim(%x) id(%x)",
__FUNCTION__, hh->prim, hh->dinfo);
2001-02-21 19:22:35 +00:00
return(-ENODEV);
}
2001-08-02 14:51:56 +00:00
ret = plci_l3l4(plci, hh->prim, skb);
2001-02-21 19:22:35 +00:00
}
2001-08-02 14:51:56 +00:00
return(ret);
2001-02-21 19:22:35 +00:00
}
2001-08-02 14:51:56 +00:00
int contrL4L3(Contr_t *contr, u_int prim, int dinfo, struct sk_buff *skb)
{
return(if_newhead(&contr->inst.down, prim, dinfo, skb));
2001-02-22 05:54:40 +00:00
}
2001-02-21 19:22:35 +00:00
void contrPutStatus(Contr_t *contr, char *msg)
{
printk(KERN_DEBUG "HiSax: %s", msg);
}
2001-03-26 11:40:02 +00:00
Contr_t *newContr(hisaxobject_t *ocapi, hisaxstack_t *st, hisax_pid_t *pid)
2001-02-21 19:22:35 +00:00
{
Contr_t *contr;
2001-03-26 11:40:02 +00:00
if (!pid)
2001-02-21 19:22:35 +00:00
return(NULL);
if (!st) {
printk(KERN_ERR "newContr no stack\n");
return(NULL);
}
contr = kmalloc(sizeof(Contr_t), GFP_KERNEL);
if (!contr)
return(NULL);
2001-03-26 11:40:02 +00:00
if (contrConstr(contr, st, pid, ocapi) != 0) {
2001-02-22 05:54:40 +00:00
contrDestr(contr);
2001-02-21 19:22:35 +00:00
kfree(contr);
return(NULL);
}
return contr;
}
2001-11-14 10:41:26 +00:00
BInst_t *contrSelChannel(Contr_t *contr, u_int channel)
2001-02-27 17:45:44 +00:00
{
2001-11-14 10:41:26 +00:00
hisaxstack_t *cst;
BInst_t *binst;
channel_info_t ci;
int ret;
2001-02-27 17:45:44 +00:00
if (!contr->binst) {
cst = contr->inst.st->child;
if (!cst)
return(NULL);
while(cst) {
if (!(binst = kmalloc(sizeof(BInst_t), GFP_ATOMIC))) {
2001-02-27 17:45:44 +00:00
printk(KERN_ERR "no mem for Binst\n");
int_error();
return(NULL);
}
memset(binst, 0, sizeof(BInst_t));
2001-10-31 23:04:42 +00:00
binst->bst = cst;
2001-02-27 17:45:44 +00:00
binst->inst.st = cst;
binst->inst.data = binst;
binst->inst.obj = contr->inst.obj;
2001-03-26 11:40:02 +00:00
binst->inst.pid.layermask = ISDN_LAYER(4);
2001-02-27 17:45:44 +00:00
binst->inst.down.stat = IF_NOACTIV;
APPEND_TO_LIST(binst, contr->binst);
cst = cst->next;
}
}
2001-11-14 10:41:26 +00:00
ci.channel = channel;
ci.st.p = NULL;
ret = contr->inst.obj->ctrl(contr->inst.st, MGR_SELCHANNEL | REQUEST,
&ci);
if (ret) {
int_errtxt("ret(%d)", ret);
return(NULL);
}
cst = ci.st.p;
binst = contr->binst;
while(binst) {
if (cst == binst->bst)
break;
binst = binst->next;
}
2001-02-27 17:45:44 +00:00
return(binst);
}
2001-02-21 19:22:35 +00:00
#if 0
static void d2_listener(struct IsdnCardState *cs, u_char *buf, int len)
{
Contr_t *contr = cs->contr;
if (!contr) {
int_error();
return;
}
contrD2Trace(contr, buf, len);
}
#endif