2001-02-21 19:22:35 +00:00
|
|
|
/* $Id$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2003-07-21 12:44:46 +00:00
|
|
|
#include "capi.h"
|
2001-02-21 19:22:35 +00:00
|
|
|
#include "helper.h"
|
|
|
|
#include "debug.h"
|
2003-11-11 20:31:35 +00:00
|
|
|
#include "mISDNManufacturer.h"
|
2001-02-21 19:22:35 +00:00
|
|
|
|
|
|
|
#define applDebug(appl, lev, fmt, args...) \
|
2001-02-22 05:54:40 +00:00
|
|
|
capidebug(lev, fmt, ## args)
|
2001-02-21 19:22:35 +00:00
|
|
|
|
|
|
|
void applConstr(Appl_t *appl, Contr_t *contr, __u16 ApplId, capi_register_params *rp)
|
|
|
|
{
|
|
|
|
memset(appl, 0, sizeof(Appl_t));
|
|
|
|
appl->contr = contr;
|
|
|
|
appl->ApplId = ApplId;
|
|
|
|
appl->MsgId = 1;
|
|
|
|
appl->NotificationMask = 0;
|
|
|
|
memcpy(&appl->rp, rp, sizeof(capi_register_params));
|
|
|
|
listenConstr(&appl->listen, contr, ApplId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void applDestr(Appl_t *appl)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
listenDestr(&appl->listen);
|
|
|
|
for (i = 0; i < CAPI_MAXPLCI; i++) {
|
|
|
|
if (appl->cplcis[i]) {
|
|
|
|
cplciDestr(appl->cplcis[i]);
|
|
|
|
kfree(appl->cplcis[i]);
|
2001-02-27 17:45:44 +00:00
|
|
|
appl->cplcis[i] = NULL;
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Cplci_t *applAdr2cplci(Appl_t *appl, __u32 adr)
|
|
|
|
{
|
|
|
|
int i = (adr >> 8) & 0xff;
|
|
|
|
|
|
|
|
if ((i < 1) || (i > CAPI_MAXPLCI)) {
|
|
|
|
int_error();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return appl->cplcis[i - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void applSendMessage(Appl_t *appl, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
Plci_t *plci;
|
|
|
|
Cplci_t *cplci;
|
|
|
|
|
|
|
|
switch (CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data))) {
|
|
|
|
|
|
|
|
// for NCCI state machine
|
|
|
|
case CAPI_DATA_B3_REQ:
|
|
|
|
case CAPI_DATA_B3_RESP:
|
|
|
|
case CAPI_CONNECT_B3_REQ:
|
|
|
|
case CAPI_CONNECT_B3_RESP:
|
|
|
|
case CAPI_CONNECT_B3_ACTIVE_RESP:
|
|
|
|
case CAPI_DISCONNECT_B3_REQ:
|
|
|
|
case CAPI_DISCONNECT_B3_RESP:
|
|
|
|
cplci = applAdr2cplci(appl, CAPIMSG_CONTROL(skb->data));
|
|
|
|
if (!cplci) {
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiIllContrPlciNcci);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
if (!cplci->ncci) {
|
|
|
|
int_error();
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiIllContrPlciNcci);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
ncciSendMessage(cplci->ncci, skb);
|
|
|
|
break;
|
|
|
|
// for PLCI state machine
|
|
|
|
case CAPI_INFO_REQ:
|
|
|
|
case CAPI_ALERT_REQ:
|
|
|
|
case CAPI_CONNECT_RESP:
|
|
|
|
case CAPI_CONNECT_ACTIVE_RESP:
|
|
|
|
case CAPI_DISCONNECT_REQ:
|
|
|
|
case CAPI_DISCONNECT_RESP:
|
|
|
|
case CAPI_SELECT_B_PROTOCOL_REQ:
|
|
|
|
cplci = applAdr2cplci(appl, CAPIMSG_CONTROL(skb->data));
|
|
|
|
if (!cplci) {
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiIllContrPlciNcci);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
cplciSendMessage(cplci, skb);
|
|
|
|
break;
|
|
|
|
case CAPI_CONNECT_REQ:
|
2003-11-11 09:59:01 +00:00
|
|
|
plci = contrNewPlci(appl->contr, MISDN_ID_ANY);
|
2001-02-21 19:22:35 +00:00
|
|
|
if (!plci) {
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiNoPlciAvailable);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
cplci = applNewCplci(appl, plci);
|
|
|
|
if (!cplci) {
|
|
|
|
contrDelPlci(appl->contr, plci);
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiNoPlciAvailable);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
cplciSendMessage(cplci, skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// for LISTEN state machine
|
|
|
|
case CAPI_LISTEN_REQ:
|
|
|
|
listenSendMessage(&appl->listen, skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// other
|
|
|
|
case CAPI_FACILITY_REQ:
|
|
|
|
applFacilityReq(appl, skb);
|
|
|
|
break;
|
|
|
|
case CAPI_FACILITY_RESP:
|
|
|
|
goto free;
|
|
|
|
case CAPI_MANUFACTURER_REQ:
|
|
|
|
applManufacturerReq(appl, skb);
|
|
|
|
break;
|
|
|
|
case CAPI_INFO_RESP:
|
|
|
|
goto free;
|
|
|
|
default:
|
2003-07-21 11:13:02 +00:00
|
|
|
applDebug(appl, CAPI_DBG_WARN, "applSendMessage: %#x %#x not handled!",
|
2001-02-21 19:22:35 +00:00
|
|
|
CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
|
|
|
|
if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_REQ)
|
|
|
|
contrAnswerMessage(appl->contr, skb,
|
|
|
|
CapiMessageNotSupportedInCurrentState);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
free:
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void applFacilityReq(Appl_t *appl, struct sk_buff *skb)
|
|
|
|
{
|
2003-06-27 15:19:42 +00:00
|
|
|
_cmsg cmsg;
|
|
|
|
Cplci_t *cplci;
|
2001-02-21 19:22:35 +00:00
|
|
|
|
2003-06-27 15:19:42 +00:00
|
|
|
capi_message2cmsg(&cmsg, skb->data);
|
2001-02-21 19:22:35 +00:00
|
|
|
switch (cmsg.FacilitySelector) {
|
2003-11-11 10:02:23 +00:00
|
|
|
case 0x0000: // Handset
|
2003-06-27 15:19:42 +00:00
|
|
|
case 0x0001: // DTMF
|
|
|
|
cplci = applAdr2cplci(appl, CAPIMSG_CONTROL(skb->data));
|
|
|
|
if (cplci && cplci->ncci) {
|
|
|
|
ncciSendMessage(cplci->ncci, skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiIllContrPlciNcci);
|
|
|
|
break;
|
|
|
|
case 0x0003: // SupplementaryServices
|
|
|
|
applSuppFacilityReq(appl, &cmsg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
int_error();
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiFacilityNotSupported);
|
|
|
|
break;
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
Cplci_t *applNewCplci(Appl_t *appl, Plci_t *plci)
|
|
|
|
{
|
|
|
|
Cplci_t *cplci;
|
|
|
|
int i = (plci->adrPLCI >> 8);
|
|
|
|
|
|
|
|
if (appl->cplcis[i - 1]) {
|
|
|
|
int_error();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
cplci = kmalloc(sizeof(Cplci_t), GFP_ATOMIC);
|
|
|
|
cplciConstr(cplci, appl, plci);
|
|
|
|
appl->cplcis[i - 1] = cplci;
|
|
|
|
plciAttachCplci(plci, cplci);
|
|
|
|
return cplci;
|
|
|
|
}
|
|
|
|
|
|
|
|
void applDelCplci(Appl_t *appl, Cplci_t *cplci)
|
|
|
|
{
|
|
|
|
int i = cplci->adrPLCI >> 8;
|
|
|
|
|
|
|
|
if ((i < 1) || (i > CAPI_MAXPLCI)) {
|
|
|
|
int_error();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (appl->cplcis[i-1] != cplci) {
|
|
|
|
int_error();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cplciDestr(cplci);
|
|
|
|
kfree(cplci);
|
2001-02-27 17:45:44 +00:00
|
|
|
appl->cplcis[i-1] = NULL;
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
|
|
|
|
2003-11-11 20:31:35 +00:00
|
|
|
#define AVM_MANUFACTURER_ID 0x214D5641 /* "AVM!" */
|
|
|
|
#define CLASS_AVM 0x00
|
|
|
|
#define FUNCTION_AVM_D2_TRACE 0x01
|
2001-02-21 19:22:35 +00:00
|
|
|
|
|
|
|
struct AVMD2Trace {
|
|
|
|
__u8 Length;
|
|
|
|
__u8 data[4];
|
|
|
|
};
|
|
|
|
|
2003-11-11 20:31:35 +00:00
|
|
|
void applManufacturerReqAVM(Appl_t *appl, _cmsg *cmsg, struct sk_buff *skb)
|
2001-02-21 19:22:35 +00:00
|
|
|
{
|
2003-11-11 20:31:35 +00:00
|
|
|
struct AVMD2Trace *at;
|
2001-02-21 19:22:35 +00:00
|
|
|
|
2003-11-11 20:31:35 +00:00
|
|
|
if (cmsg->Class != CLASS_AVM) {
|
|
|
|
applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown class %#x\n", cmsg->Class);
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return;
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
2003-11-11 20:31:35 +00:00
|
|
|
switch (cmsg->Function) {
|
|
|
|
case FUNCTION_AVM_D2_TRACE:
|
|
|
|
at = (struct AVMD2Trace *)cmsg->ManuData;
|
|
|
|
if (!at || at->Length != 4) {
|
|
|
|
int_error();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (memcmp(at->data, "\200\014\000\000", 4) == 0) {
|
|
|
|
test_and_set_bit(APPL_FLAG_D2TRACE, &appl->flags);
|
|
|
|
} else if (memcmp(at->data, "\000\000\000\000", 4) == 0) {
|
|
|
|
test_and_clear_bit(APPL_FLAG_D2TRACE, &appl->flags);
|
|
|
|
} else {
|
|
|
|
int_error();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown function %#x\n", cmsg->Function);
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2003-11-11 20:31:35 +00:00
|
|
|
void applManufacturerReqmISDN(Appl_t *appl, _cmsg *cmsg, struct sk_buff *skb)
|
2001-02-21 19:22:35 +00:00
|
|
|
{
|
2003-11-11 20:31:35 +00:00
|
|
|
Cplci_t *cplci;
|
2001-02-21 19:22:35 +00:00
|
|
|
|
2003-11-11 20:31:35 +00:00
|
|
|
switch (cmsg->Class) {
|
|
|
|
case mISDN_MF_CLASS_HANDSET:
|
|
|
|
/* Note normally MANUFATURER messages are only defined for
|
|
|
|
* controller address we extent it here to PLCI/NCCI
|
|
|
|
*/
|
|
|
|
cplci = applAdr2cplci(appl, CAPIMSG_CONTROL(skb->data));
|
|
|
|
if (cplci && cplci->ncci) {
|
|
|
|
ncciSendMessage(cplci->ncci, skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
contrAnswerMessage(appl->contr, skb, CapiIllContrPlciNcci);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
break;
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void applManufacturerReq(Appl_t *appl, struct sk_buff *skb)
|
|
|
|
{
|
2003-11-11 20:31:35 +00:00
|
|
|
_cmsg cmsg;
|
|
|
|
|
2001-02-21 19:22:35 +00:00
|
|
|
if (skb->len < 16 + 8) {
|
2003-11-11 20:31:35 +00:00
|
|
|
dev_kfree_skb(skb);
|
2001-02-21 19:22:35 +00:00
|
|
|
return;
|
|
|
|
}
|
2003-11-11 20:31:35 +00:00
|
|
|
capi_message2cmsg(&cmsg, skb->data);
|
|
|
|
switch (cmsg.ManuID) {
|
|
|
|
case mISDN_MANUFACTURER_ID:
|
|
|
|
applManufacturerReqmISDN(appl, &cmsg, skb);
|
|
|
|
break;
|
|
|
|
case AVM_MANUFACTURER_ID:
|
|
|
|
applManufacturerReqAVM(appl, &cmsg, skb);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown ManuID %#x\n", cmsg.ManuID);
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
break;
|
2001-02-21 19:22:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void applD2Trace(Appl_t *appl, u_char *buf, int len)
|
|
|
|
{
|
|
|
|
_cmsg cmsg;
|
|
|
|
__u8 manuData[255];
|
|
|
|
|
|
|
|
if (!test_bit(APPL_FLAG_D2TRACE, &appl->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
memset(&cmsg, 0, sizeof(_cmsg));
|
|
|
|
capi_cmsg_header(&cmsg, appl->ApplId, CAPI_MANUFACTURER, CAPI_IND,
|
|
|
|
appl->MsgId++, appl->contr->adrController);
|
2003-11-11 20:31:35 +00:00
|
|
|
cmsg.ManuID = AVM_MANUFACTURER_ID;
|
2001-02-21 19:22:35 +00:00
|
|
|
cmsg.Class = CLASS_AVM;
|
|
|
|
cmsg.Function = FUNCTION_AVM_D2_TRACE;
|
|
|
|
cmsg.ManuData = (_cstruct) &manuData;
|
|
|
|
manuData[0] = 2 + len; // length
|
|
|
|
manuData[1] = 0x80;
|
|
|
|
manuData[2] = 0x0f;
|
|
|
|
memcpy(&manuData[3], buf, len);
|
|
|
|
|
|
|
|
contrRecvCmsg(appl->contr, &cmsg);
|
|
|
|
}
|