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:
Richard Mudgett 2009-11-10 19:27:34 +00:00
parent c7c670aa85
commit e23ea9568c
4 changed files with 239 additions and 81 deletions

View File

@ -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
View File

@ -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)

View File

@ -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
View File

@ -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;
}