Add dummy call reference support.
Fixes problem where PTMP NT mode responds erroneously to a FACILITY message from a phone on the dummy call reference. LibPRI behaved as if the dummy call reference were an invalid call reference and proceeded to respond on the global call reference. git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1275 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
parent
c7c670aa85
commit
e23ea9568c
8
libpri.h
8
libpri.h
@ -929,6 +929,14 @@ extern int pri_maintenance_service(struct pri *pri, int span, int channel, int c
|
||||
/* Create a new call */
|
||||
q931_call *pri_new_call(struct pri *pri);
|
||||
|
||||
/*!
|
||||
* \brief Deterimine if the given call control pointer is a dummy call.
|
||||
*
|
||||
* \retval TRUE if given call is a dummy call.
|
||||
* \retval FALSE otherwise.
|
||||
*/
|
||||
int pri_is_dummy_call(q931_call *call);
|
||||
|
||||
/* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */
|
||||
int pri_get_crv(struct pri *pri, q931_call *call, int *callmode);
|
||||
|
||||
|
59
pri.c
59
pri.c
@ -226,18 +226,44 @@ static int __pri_write(struct pri *pri, void *buf, int buflen)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Pass in the master for this function */
|
||||
void __pri_free_tei(struct pri * p)
|
||||
{
|
||||
if (p) {
|
||||
struct q931_call *call;
|
||||
|
||||
call = p->dummy_call;
|
||||
if (call) {
|
||||
pri_schedule_del(call->pri, call->retranstimer);
|
||||
pri_call_apdu_queue_cleanup(call);
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, pri_io_cb rd, pri_io_cb wr, void *userdata, int tei, int bri)
|
||||
{
|
||||
struct d_ctrl_dummy *dummy_ctrl;
|
||||
struct pri *p;
|
||||
|
||||
if (!(p = calloc(1, sizeof(*p))))
|
||||
switch (switchtype) {
|
||||
case PRI_SWITCH_GR303_EOC:
|
||||
case PRI_SWITCH_GR303_TMC:
|
||||
case PRI_SWITCH_GR303_TMC_SWITCHING:
|
||||
case PRI_SWITCH_GR303_EOC_PATH:
|
||||
p = calloc(1, sizeof(*p));
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
dummy_ctrl = NULL;
|
||||
break;
|
||||
default:
|
||||
dummy_ctrl = calloc(1, sizeof(*dummy_ctrl));
|
||||
if (!dummy_ctrl) {
|
||||
return NULL;
|
||||
}
|
||||
p = &dummy_ctrl->ctrl;
|
||||
break;
|
||||
}
|
||||
|
||||
p->bri = bri;
|
||||
p->fd = fd;
|
||||
@ -265,7 +291,14 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master,
|
||||
p->q931_rxcount = 0;
|
||||
p->q931_txcount = 0;
|
||||
#endif
|
||||
if (switchtype == PRI_SWITCH_GR303_EOC) {
|
||||
if (dummy_ctrl) {
|
||||
/* Initialize the dummy call reference call record. */
|
||||
dummy_ctrl->ctrl.dummy_call = &dummy_ctrl->dummy_call;
|
||||
q931_init_call_record(&dummy_ctrl->ctrl, dummy_ctrl->ctrl.dummy_call,
|
||||
Q931_DUMMY_CALL_REFERENCE);
|
||||
}
|
||||
switch (switchtype) {
|
||||
case PRI_SWITCH_GR303_EOC:
|
||||
p->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
|
||||
p->sapi = Q921_SAPI_GR303_EOC;
|
||||
p->tei = Q921_TEI_GR303_EOC_OPS;
|
||||
@ -274,7 +307,8 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master,
|
||||
free(p);
|
||||
p = NULL;
|
||||
}
|
||||
} else if (switchtype == PRI_SWITCH_GR303_TMC) {
|
||||
break;
|
||||
case PRI_SWITCH_GR303_TMC:
|
||||
p->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
|
||||
p->sapi = Q921_SAPI_GR303_TMC_CALLPROC;
|
||||
p->tei = Q921_TEI_GR303_TMC_CALLPROC;
|
||||
@ -283,14 +317,19 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master,
|
||||
free(p);
|
||||
p = NULL;
|
||||
}
|
||||
} else if (switchtype == PRI_SWITCH_GR303_TMC_SWITCHING) {
|
||||
break;
|
||||
case PRI_SWITCH_GR303_TMC_SWITCHING:
|
||||
p->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
|
||||
p->sapi = Q921_SAPI_GR303_TMC_SWITCHING;
|
||||
p->tei = Q921_TEI_GR303_TMC_SWITCHING;
|
||||
} else if (switchtype == PRI_SWITCH_GR303_EOC_PATH) {
|
||||
break;
|
||||
case PRI_SWITCH_GR303_EOC_PATH:
|
||||
p->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
|
||||
p->sapi = Q921_SAPI_GR303_EOC;
|
||||
p->tei = Q921_TEI_GR303_EOC_PATH;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Start Q.921 layer, Wait if we're the network */
|
||||
if (p)
|
||||
@ -935,6 +974,14 @@ q931_call *pri_new_call(struct pri *pri)
|
||||
return q931_new_call(pri);
|
||||
}
|
||||
|
||||
int pri_is_dummy_call(q931_call *call)
|
||||
{
|
||||
if (!call) {
|
||||
return 0;
|
||||
}
|
||||
return q931_is_dummy_call(call);
|
||||
}
|
||||
|
||||
void pri_dump_event(struct pri *pri, pri_event *e)
|
||||
{
|
||||
if (!pri || !e)
|
||||
|
@ -114,6 +114,13 @@ struct pri {
|
||||
q931_call **callpool;
|
||||
q931_call *localpool;
|
||||
|
||||
/*!
|
||||
* \brief Q.931 Dummy call reference call associated with this TEI.
|
||||
* \note If present then this call is allocated as part of the
|
||||
* D channel control structure.
|
||||
*/
|
||||
q931_call *dummy_call;
|
||||
|
||||
/* do we do overlap dialing */
|
||||
int overlapdial;
|
||||
|
||||
@ -506,6 +513,14 @@ struct q931_call {
|
||||
int pri_winner;
|
||||
};
|
||||
|
||||
/*! D channel control structure with associated dummy call reference record. */
|
||||
struct d_ctrl_dummy {
|
||||
/*! D channel control structure. */
|
||||
struct pri ctrl;
|
||||
/*! Dummy call reference call record. */
|
||||
struct q931_call dummy_call;
|
||||
};
|
||||
|
||||
extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data);
|
||||
|
||||
extern pri_event *pri_schedule_run(struct pri *pri);
|
||||
@ -523,6 +538,8 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master,
|
||||
|
||||
void __pri_free_tei(struct pri *p);
|
||||
|
||||
void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr);
|
||||
|
||||
void q931_party_name_init(struct q931_party_name *name);
|
||||
void q931_party_number_init(struct q931_party_number *number);
|
||||
void q931_party_subaddress_init(struct q931_party_subaddress *subaddr);
|
||||
@ -589,4 +606,17 @@ static inline int BRI_TE_PTMP(struct pri *mypri)
|
||||
return pri->bri && (((pri)->localtype == PRI_CPE) && ((pri)->tei == Q921_TEI_GROUP));
|
||||
}
|
||||
|
||||
#define Q931_DUMMY_CALL_REFERENCE -1
|
||||
|
||||
/*!
|
||||
* \brief Deterimine if the given call control pointer is a dummy call.
|
||||
*
|
||||
* \retval TRUE if given call is a dummy call.
|
||||
* \retval FALSE otherwise.
|
||||
*/
|
||||
static inline int q931_is_dummy_call(const q931_call *call)
|
||||
{
|
||||
return (call->cr == Q931_DUMMY_CALL_REFERENCE) ? 1 : 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
187
q931.c
187
q931.c
@ -3307,17 +3307,20 @@ static char *maintenance_msg2str(int msg, int pd)
|
||||
return "Unknown Message Type";
|
||||
}
|
||||
|
||||
/* Decode the call reference */
|
||||
static inline int q931_cr(q931_h *h)
|
||||
{
|
||||
int cr = 0;
|
||||
int cr;
|
||||
int x;
|
||||
|
||||
if (h->crlen > 3) {
|
||||
pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen);
|
||||
return -1;
|
||||
return Q931_DUMMY_CALL_REFERENCE;
|
||||
}
|
||||
switch (h->crlen) {
|
||||
case 2:
|
||||
for (x=0;x<h->crlen;x++) {
|
||||
cr = 0;
|
||||
for (x = 0; x < h->crlen; ++x) {
|
||||
cr <<= 8;
|
||||
cr |= h->crv[x];
|
||||
}
|
||||
@ -3329,8 +3332,13 @@ static inline int q931_cr(q931_h *h)
|
||||
cr |= 0x8000;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
cr = Q931_DUMMY_CALL_REFERENCE;
|
||||
break;
|
||||
default:
|
||||
pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen);
|
||||
cr = Q931_DUMMY_CALL_REFERENCE;
|
||||
break;
|
||||
}
|
||||
return cr;
|
||||
}
|
||||
@ -3370,12 +3378,73 @@ static inline void q931_dumpie(struct pri *ctrl, int codeset, q931_ie *ie, char
|
||||
pri_error(ctrl, "!! %c Unknown IE %d (cs%d, len = %d)\n", prefix, Q931_IE_IE(base_ie), Q931_IE_CODESET(base_ie), ielen(ie));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Initialize the call record.
|
||||
*
|
||||
* \param ctrl D channel controller.
|
||||
* \param call Q.931 call leg.
|
||||
*
|
||||
* \note The call record is assumed to already be memset() to zero.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr)
|
||||
{
|
||||
call->cr = cr;
|
||||
call->slotmap = -1;
|
||||
call->channelno = -1;
|
||||
if (cr != Q931_DUMMY_CALL_REFERENCE) {
|
||||
call->newcall = 1;
|
||||
}
|
||||
call->ourcallstate = Q931_CALL_STATE_NULL;
|
||||
call->peercallstate = Q931_CALL_STATE_NULL;
|
||||
call->sugcallstate = Q931_CALL_STATE_NOT_SET;
|
||||
call->ri = -1;
|
||||
call->transcapability = -1;
|
||||
call->transmoderate = -1;
|
||||
call->transmultiple = -1;
|
||||
call->userl1 = -1;
|
||||
call->userl2 = -1;
|
||||
call->userl3 = -1;
|
||||
call->rateadaption = -1;
|
||||
call->progress = -1;
|
||||
call->causecode = -1;
|
||||
call->causeloc = -1;
|
||||
call->cause = -1;
|
||||
call->useruserprotocoldisc = -1;
|
||||
call->aoc_units = -1;
|
||||
call->changestatus = -1;
|
||||
call->reversecharge = -1;
|
||||
call->pri_winner = -1;
|
||||
call->master_call = call;
|
||||
q931_party_number_init(&call->redirection_number);
|
||||
q931_party_address_init(&call->called);
|
||||
q931_party_id_init(&call->local_id);
|
||||
q931_party_id_init(&call->remote_id);
|
||||
q931_party_redirecting_init(&call->redirecting);
|
||||
|
||||
/* PRI is set to whoever called us */
|
||||
if (BRI_TE_PTMP(ctrl)) {
|
||||
/*
|
||||
* Point to the master to avoid stale pointer problems if
|
||||
* the TEI is removed later.
|
||||
*/
|
||||
call->pri = PRI_MASTER(ctrl);
|
||||
} else {
|
||||
call->pri = ctrl;
|
||||
}
|
||||
}
|
||||
|
||||
static q931_call *q931_getcall(struct pri *ctrl, int cr)
|
||||
{
|
||||
q931_call *cur;
|
||||
q931_call *prev;
|
||||
struct pri *master;
|
||||
|
||||
if (cr == Q931_DUMMY_CALL_REFERENCE) {
|
||||
return ctrl->dummy_call;
|
||||
}
|
||||
|
||||
/* Find the master - He has the call pool */
|
||||
master = PRI_MASTER(ctrl);
|
||||
|
||||
@ -3414,47 +3483,7 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr)
|
||||
}
|
||||
|
||||
/* Initialize call structure. */
|
||||
cur->cr = cr;
|
||||
cur->slotmap = -1;
|
||||
cur->channelno = -1;
|
||||
cur->newcall = 1;
|
||||
cur->ourcallstate = Q931_CALL_STATE_NULL;
|
||||
cur->peercallstate = Q931_CALL_STATE_NULL;
|
||||
cur->sugcallstate = Q931_CALL_STATE_NOT_SET;
|
||||
cur->ri = -1;
|
||||
cur->transcapability = -1;
|
||||
cur->transmoderate = -1;
|
||||
cur->transmultiple = -1;
|
||||
cur->userl1 = -1;
|
||||
cur->userl2 = -1;
|
||||
cur->userl3 = -1;
|
||||
cur->rateadaption = -1;
|
||||
cur->progress = -1;
|
||||
cur->causecode = -1;
|
||||
cur->causeloc = -1;
|
||||
cur->cause = -1;
|
||||
cur->useruserprotocoldisc = -1;
|
||||
cur->aoc_units = -1;
|
||||
cur->changestatus = -1;
|
||||
cur->reversecharge = -1;
|
||||
cur->pri_winner = -1;
|
||||
cur->master_call = cur;
|
||||
q931_party_number_init(&cur->redirection_number);
|
||||
q931_party_address_init(&cur->called);
|
||||
q931_party_id_init(&cur->local_id);
|
||||
q931_party_id_init(&cur->remote_id);
|
||||
q931_party_redirecting_init(&cur->redirecting);
|
||||
|
||||
/* PRI is set to whoever called us */
|
||||
if (BRI_TE_PTMP(ctrl)) {
|
||||
/*
|
||||
* Point to the master to avoid stale pointer problems if
|
||||
* the TEI is removed later.
|
||||
*/
|
||||
cur->pri = master;
|
||||
} else {
|
||||
cur->pri = ctrl;
|
||||
}
|
||||
q931_init_call_record(ctrl, cur, cr);
|
||||
|
||||
/* Append to end of list */
|
||||
if (prev) {
|
||||
@ -3511,6 +3540,10 @@ void q931_destroycall(struct pri *ctrl, q931_call *c)
|
||||
int slavesleft;
|
||||
int slaveidx;
|
||||
|
||||
if (q931_is_dummy_call(c)) {
|
||||
/* Cannot destroy the dummy call. */
|
||||
return;
|
||||
}
|
||||
if (c->master_call != c) {
|
||||
slave = c;
|
||||
c = slave->master_call;
|
||||
@ -3711,7 +3744,9 @@ void q931_dump(struct pri *ctrl, q931_h *h, int len, int txrx)
|
||||
cref = q931_cr(h);
|
||||
pri_message(ctrl, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n",
|
||||
c, h->crlen, cref & 0x7FFF, cref & 0x7FFF,
|
||||
(cref & 0x8000) ? "Terminator" : "Originator");
|
||||
(cref == Q931_DUMMY_CALL_REFERENCE)
|
||||
? "Dummy"
|
||||
: (cref & 0x8000) ? "Terminator" : "Originator");
|
||||
|
||||
/* Message header begins at the end of the call reference number */
|
||||
mh = (q931_mh *)(h->contents + h->crlen);
|
||||
@ -3778,7 +3813,9 @@ static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q
|
||||
h->pd = ctrl->protodisc;
|
||||
}
|
||||
h->x0 = 0; /* Reserved 0 */
|
||||
if (!ctrl->bri) {
|
||||
if (q931_is_dummy_call(call)) {
|
||||
h->crlen = 0;
|
||||
} else if (!ctrl->bri) {
|
||||
/* Two bytes of Call Reference. */
|
||||
h->crlen = 2;
|
||||
/* Invert the top bit to make it from our sense */
|
||||
@ -3807,18 +3844,11 @@ static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q
|
||||
|
||||
static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe)
|
||||
{
|
||||
/*
|
||||
* For NT-PTMP mode, we need to check the following:
|
||||
* MODE = NT-PTMP
|
||||
* MESSAGE = SETUP
|
||||
*
|
||||
* If those are true, we need to send the SETUP in a UI frame
|
||||
* instead of an I-frame.
|
||||
*/
|
||||
if (BRI_NT_PTMP(ctrl) && uiframe)
|
||||
if (uiframe) {
|
||||
q921_transmit_uiframe(ctrl, h, len);
|
||||
else
|
||||
} else {
|
||||
q921_transmit_iframe(ctrl, h, len, cr);
|
||||
}
|
||||
/* The transmit operation might dump the q921 header, so logging the q931
|
||||
message body after the transmit puts the sections of the message in the
|
||||
right order in the log */
|
||||
@ -3893,6 +3923,43 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[
|
||||
ctrl = ctrl->subchannel;
|
||||
}
|
||||
if (ctrl) {
|
||||
int uiframe;
|
||||
|
||||
switch (msgtype) {
|
||||
case Q931_SETUP:
|
||||
/*
|
||||
* For NT-PTMP mode, we need to check the following:
|
||||
* MODE = NT-PTMP
|
||||
* MESSAGE = SETUP
|
||||
*
|
||||
* If those are true, we need to send the SETUP in a UI frame
|
||||
* instead of an I-frame.
|
||||
*/
|
||||
uiframe = call->outboundbroadcast;
|
||||
break;
|
||||
case Q931_FACILITY:
|
||||
if (ctrl->tei == Q921_TEI_GROUP) {
|
||||
/* Broadcast TEI. */
|
||||
if (q931_is_dummy_call(call)) {
|
||||
/*
|
||||
* This is a FACILITY message on the dummy call reference
|
||||
* for the broadcast TEI.
|
||||
*/
|
||||
uiframe = 1;
|
||||
} else {
|
||||
pri_error(ctrl,
|
||||
"Attempting to broadcast %s on cref %d\n",
|
||||
msg2str(msgtype), call->cr);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
uiframe = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
uiframe = 0;
|
||||
break;
|
||||
}
|
||||
if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
|
||||
pri_message(ctrl,
|
||||
"Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n",
|
||||
@ -3900,7 +3967,7 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[
|
||||
ctrl, ctrl->tei, ctrl->sapi,
|
||||
call->pri, call->pri->tei, call->pri->sapi);
|
||||
}
|
||||
q931_xmit(ctrl, h, len, 1, (msgtype == Q931_SETUP) ? 1 : 0);
|
||||
q931_xmit(ctrl, h, len, 1, uiframe);
|
||||
}
|
||||
call->acked = 1;
|
||||
return 0;
|
||||
@ -5375,6 +5442,9 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca
|
||||
c->ri = -1;
|
||||
break;
|
||||
case Q931_FACILITY:
|
||||
if (q931_is_dummy_call(c)) {
|
||||
q931_party_address_init(&c->called);
|
||||
}
|
||||
break;
|
||||
case Q931_SETUP:
|
||||
if (ctrl->debug & PRI_DEBUG_Q931_STATE)
|
||||
@ -7288,7 +7358,10 @@ int q931_call_getcrv(struct pri *ctrl, q931_call *call, int *callmode)
|
||||
|
||||
int q931_call_setcrv(struct pri *ctrl, q931_call *call, int crv, int callmode)
|
||||
{
|
||||
/* Do not allow changing the dummy call reference */
|
||||
if (!q931_is_dummy_call(call)) {
|
||||
call->cr = (crv << 3) & 0x7fff;
|
||||
call->cr |= (callmode & 0x7);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user