Add BRI PTMP NT mode, HOLD/RETRIEVE, Call rerouting/deflection, and keypad facility support.

* Added support for BRI PTMP NT mode.  (Overlap dialing NT -> TE not supported.)
* Added handling of received HOLD/RETRIEVE messages and the optional ability
  to transfer a held call on disconnect similar to an analog phone.
* Added CallRerouting/CallDeflection support for Q.SIG, ETSI PTP, ETSI PTMP.
  Will reroute/deflect an outgoing call when receive the message.
  Can use the DAHDISendCallreroutingFacility to send the message for the
  supported switches.
* Added ability to send/receive keypad digits in the SETUP message.
  Send keypad digits in SETUP message: Dial(DAHDI/g1[/K<keypad_digits>][/extension])
  Access any received keypad digits in SETUP message by: ${CHANNEL(keypad_digits)}

(closes issue #15048)
Tested by: rmudgett, mattf


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1242 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
Richard Mudgett 2009-10-23 19:50:45 +00:00
parent a78ee730c9
commit a2dcb6adba
9 changed files with 3338 additions and 221 deletions

277
libpri.h
View File

@ -91,6 +91,12 @@
#define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */
#define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */ #define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */
#define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */ #define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */
#define PRI_EVENT_HOLD 21 /* HOLD request received */
#define PRI_EVENT_HOLD_ACK 22 /* HOLD_ACKNOWLEDGE received */
#define PRI_EVENT_HOLD_REJ 23 /* HOLD_REJECT received */
#define PRI_EVENT_RETRIEVE 24 /* RETRIEVE request received */
#define PRI_EVENT_RETRIEVE_ACK 25 /* RETRIEVE_ACKNOWLEDGE received */
#define PRI_EVENT_RETRIEVE_REJ 26 /* RETRIEVE_REJECT received */
/* Simple states */ /* Simple states */
#define PRI_STATE_DOWN 0 #define PRI_STATE_DOWN 0
@ -200,6 +206,7 @@
#define PRI_CAUSE_NO_ANSWER 19 #define PRI_CAUSE_NO_ANSWER 19
#define PRI_CAUSE_CALL_REJECTED 21 #define PRI_CAUSE_CALL_REJECTED 21
#define PRI_CAUSE_NUMBER_CHANGED 22 #define PRI_CAUSE_NUMBER_CHANGED 22
#define PRI_CAUSE_NONSELECTED_USER_CLEARING 26
#define PRI_CAUSE_DESTINATION_OUT_OF_ORDER 27 #define PRI_CAUSE_DESTINATION_OUT_OF_ORDER 27
#define PRI_CAUSE_INVALID_NUMBER_FORMAT 28 #define PRI_CAUSE_INVALID_NUMBER_FORMAT 28
#define PRI_CAUSE_FACILITY_REJECTED 29 /* !Q.SIG */ #define PRI_CAUSE_FACILITY_REJECTED 29 /* !Q.SIG */
@ -212,6 +219,7 @@
#define PRI_CAUSE_ACCESS_INFO_DISCARDED 43 /* !Q.SIG */ #define PRI_CAUSE_ACCESS_INFO_DISCARDED 43 /* !Q.SIG */
#define PRI_CAUSE_REQUESTED_CHAN_UNAVAIL 44 #define PRI_CAUSE_REQUESTED_CHAN_UNAVAIL 44
#define PRI_CAUSE_PRE_EMPTED 45 /* !Q.SIG */ #define PRI_CAUSE_PRE_EMPTED 45 /* !Q.SIG */
#define PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED 47
#define PRI_CAUSE_FACILITY_NOT_SUBSCRIBED 50 /* !Q.SIG */ #define PRI_CAUSE_FACILITY_NOT_SUBSCRIBED 50 /* !Q.SIG */
#define PRI_CAUSE_OUTGOING_CALL_BARRED 52 /* !Q.SIG */ #define PRI_CAUSE_OUTGOING_CALL_BARRED 52 /* !Q.SIG */
#define PRI_CAUSE_INCOMING_CALL_BARRED 54 /* !Q.SIG */ #define PRI_CAUSE_INCOMING_CALL_BARRED 54 /* !Q.SIG */
@ -468,9 +476,37 @@ struct pri_party_redirecting {
int reason; int reason;
}; };
/*!
* \brief Information for rerouting/deflecting the call.
*/
struct pri_rerouting_data {
/*!
* \brief Updated caller-id information.
* \note The information may have been altered by procedure in the private network.
*/
struct pri_party_id caller;
/*!
* \note
* deflection.to is the new called number and must always be present.
*/
struct pri_party_redirecting deflection;
/*!
* \brief Diverting user subscription option to specify if caller is notified.
* \details
* noNotification(0),
* notificationWithoutDivertedToNr(1),
* notificationWithDivertedToNr(2),
* notApplicable(3) (Status only.)
*/
int subscription_option;
/*! Invocation ID to use when sending a reply to the call rerouting/deflection request. */
int invoke_id;
};
/* Subcommands derived from supplementary services. */ /* Subcommands derived from supplementary services. */
#define PRI_SUBCMD_REDIRECTING 1 #define PRI_SUBCMD_REDIRECTING 1
#define PRI_SUBCMD_CONNECTED_LINE 2 #define PRI_SUBCMD_CONNECTED_LINE 2
#define PRI_SUBCMD_REROUTING 3
struct pri_subcommand { struct pri_subcommand {
@ -481,6 +517,7 @@ struct pri_subcommand {
char reserve_space[512]; char reserve_space[512];
struct pri_party_connected_line connected_line; struct pri_party_connected_line connected_line;
struct pri_party_redirecting redirecting; struct pri_party_redirecting redirecting;
struct pri_rerouting_data rerouting;
} u; } u;
}; };
@ -497,13 +534,14 @@ struct pri_subcommands {
* Event channel parameter encoding: * Event channel parameter encoding:
* 3322 2222 2222 1111 1111 1100 0000 0000 * 3322 2222 2222 1111 1111 1100 0000 0000
* 1098 7654 3210 9876 5432 1098 7654 3210 * 1098 7654 3210 9876 5432 1098 7654 3210
* xxxx xxxx xxxx xxDC BBBBBBBBB AAAAAAAAA * xxxx xxxx xxxx xEDC BBBBBBBBB AAAAAAAAA
* *
* Bit field * Bit field
* A - B channel * A - B channel
* B - Span (DS1) (0 - 127) * B - Span (DS1) (0 - 127)
* C - DS1 Explicit bit * C - DS1 Explicit bit
* D - D channel (cis_call) bit (status only) * D - D channel (cis_call) bit (status only)
* E - Call is held bit (status only)
* *
* B channel values: * B channel values:
* 0 - No channel (ISDN uses for call waiting feature) * 0 - No channel (ISDN uses for call waiting feature)
@ -567,10 +605,16 @@ struct pri_event_facility {
char callingnum[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ char callingnum[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */
int channel; int channel;
int cref; int cref;
/*!
* \brief Master call or normal call.
* \note Call pointer known about by upper layer.
* \note NULL if dummy call reference.
*/
q931_call *call; q931_call *call;
int callingpres; /*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */ int callingpres; /*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */
int callingplan; /*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */ int callingplan; /*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */
struct pri_subcommands *subcmds; struct pri_subcommands *subcmds;
q931_call *subcall; /*!< Subcall to send any reply toward. */
}; };
#define PRI_CALLINGPLANANI #define PRI_CALLINGPLANANI
@ -609,6 +653,7 @@ typedef struct pri_event_ring {
struct pri_subcommands *subcmds; struct pri_subcommands *subcmds;
struct pri_party_id calling; /* Calling Party's info, initially subaddress' */ struct pri_party_id calling; /* Calling Party's info, initially subaddress' */
struct pri_party_subaddress called_subaddress; /* Called party's subaddress */ struct pri_party_subaddress called_subaddress; /* Called party's subaddress */
char keypad_digits[64]; /* Keypad digits in the SETUP message. */
} pri_event_ring; } pri_event_ring;
typedef struct pri_event_hangup { typedef struct pri_event_hangup {
@ -616,10 +661,22 @@ typedef struct pri_event_hangup {
int channel; /* Channel requested */ int channel; /* Channel requested */
int cause; int cause;
int cref; int cref;
q931_call *call; /* Opaque call pointer */ q931_call *call; /* Opaque call pointer of call hanging up. */
long aoc_units; /* Advise of Charge number of charged units */ long aoc_units; /* Advise of Charge number of charged units */
char useruserinfo[260]; /* User->User info */ char useruserinfo[260]; /* User->User info */
struct pri_subcommands *subcmds; struct pri_subcommands *subcmds;
/*!
* \brief Opaque held call pointer for possible transfer to active call.
* \note The call_held and call_active pointers must not be NULL if
* transfer held call on disconnect is available.
*/
q931_call *call_held;
/*!
* \brief Opaque active call pointer for possible transfer with held call.
* \note The call_held and call_active pointers must not be NULL if
* transfer held call on disconnect is available.
*/
q931_call *call_active;
} pri_event_hangup; } pri_event_hangup;
typedef struct pri_event_restart_ack { typedef struct pri_event_restart_ack {
@ -651,6 +708,7 @@ typedef struct pri_event_notify {
int channel; int channel;
int info; int info;
struct pri_subcommands *subcmds; struct pri_subcommands *subcmds;
q931_call *call;
} pri_event_notify; } pri_event_notify;
typedef struct pri_event_keypad_digit { typedef struct pri_event_keypad_digit {
@ -673,6 +731,51 @@ typedef struct pri_event_service_ack {
int changestatus; int changestatus;
} pri_event_service_ack; } pri_event_service_ack;
struct pri_event_hold {
int e;
int channel;
q931_call *call;
struct pri_subcommands *subcmds;
};
struct pri_event_hold_ack {
int e;
int channel;
q931_call *call;
struct pri_subcommands *subcmds;
};
struct pri_event_hold_rej {
int e;
int channel;
q931_call *call;
int cause;
struct pri_subcommands *subcmds;
};
struct pri_event_retrieve {
int e;
int channel;
q931_call *call;
int flexible; /* Are we flexible with our channel selection? */
struct pri_subcommands *subcmds;
};
struct pri_event_retrieve_ack {
int e;
int channel;
q931_call *call;
struct pri_subcommands *subcmds;
};
struct pri_event_retrieve_rej {
int e;
int channel;
q931_call *call;
int cause;
struct pri_subcommands *subcmds;
};
typedef union { typedef union {
int e; int e;
pri_event_generic gen; /* Generic view */ pri_event_generic gen; /* Generic view */
@ -691,6 +794,12 @@ typedef union {
pri_event_service service; /* service message */ pri_event_service service; /* service message */
pri_event_service_ack service_ack; /* service acknowledgement message */ pri_event_service_ack service_ack; /* service acknowledgement message */
struct pri_event_facility facility; struct pri_event_facility facility;
struct pri_event_hold hold;
struct pri_event_hold_ack hold_ack;
struct pri_event_hold_rej hold_rej;
struct pri_event_retrieve retrieve;
struct pri_event_retrieve_ack retrieve_ack;
struct pri_event_retrieve_rej retrieve_rej;
} pri_event; } pri_event;
struct pri; struct pri;
@ -888,6 +997,16 @@ void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_re
/*! \note Use pri_sr_set_redirecting_parties() instead to pass more precise redirecting information. */ /*! \note Use pri_sr_set_redirecting_parties() instead to pass more precise redirecting information. */
int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason);
/*!
* \brief Set the keypad digits in the call SETUP record.
*
* \param sr New call SETUP record.
* \param keypad_digits Keypad digits to send.
*
* \return Nothing
*/
void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits);
#define PRI_USER_USER_TX #define PRI_USER_USER_TX
/* Set the user user field. Warning! don't send binary data accross this field */ /* Set the user user field. Warning! don't send binary data accross this field */
void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars);
@ -973,6 +1092,157 @@ int pri_notify(struct pri *pri, q931_call *c, int channel, int info);
int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason); int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason);
/*!
* \brief Set the call deflection/rerouting feature enable flag.
*
* \param ctrl D channel controller.
* \param enable TRUE to enable call deflection/rerouting feature.
*
* \return Nothing
*/
void pri_reroute_enable(struct pri *ctrl, int enable);
/*!
* \brief Send the CallRerouting/CallDeflection message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.)
* \param deflection Call rerouting/deflecting redirection data.
* \param subscription_option Diverting user subscription option to specify if caller is notified.
*
* \note
* deflection->to is the new called number and must always be present.
* \note
* subscription option:
* noNotification(0),
* notificationWithoutDivertedToNr(1),
* notificationWithDivertedToNr(2)
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option);
enum PRI_REROUTING_RSP_CODE {
/*!
* Rerouting invocation accepted and the network provider option
* "served user call retention on invocation of diversion"
* is "clear call on invocation".
*/
PRI_REROUTING_RSP_OK_CLEAR,
/*!
* Rerouting invocation accepted and the network provider option
* "served user call retention on invocation of diversion"
* is "retain call until alerting begins at the deflected-to user".
*/
PRI_REROUTING_RSP_OK_RETAIN,
PRI_REROUTING_RSP_NOT_SUBSCRIBED,
PRI_REROUTING_RSP_NOT_AVAILABLE,
/*! Supplementary service interaction not allowed. */
PRI_REROUTING_RSP_NOT_ALLOWED,
PRI_REROUTING_RSP_INVALID_NUMBER,
/*! Deflection to prohibited number (e.g., operator, police, emergency). */
PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER,
/*! Deflection to served user number. */
PRI_REROUTING_RSP_DIVERSION_TO_SELF,
PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED,
PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE,
};
/*!
* \brief Send the CallRerouteing/CallDeflection response message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg.
* \param invoke_id Value given by the initiating request.
* \param code The result to send.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code);
/*!
* \brief Set the call hold feature enable flag.
*
* \param ctrl D channel controller.
* \param enable TRUE to enable call hold feature.
*
* \return Nothing
*/
void pri_hold_enable(struct pri *ctrl, int enable);
/*!
* \brief Send the HOLD message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_hold(struct pri *ctrl, q931_call *call);
/*!
* \brief Send the HOLD ACKNOWLEDGE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_hold_ack(struct pri *ctrl, q931_call *call);
/*!
* \brief Send the HOLD REJECT message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param cause Q.931 cause code for rejecting the hold request.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause);
/*!
* \brief Send the RETRIEVE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param channel Encoded channel id to use. If zero do not send channel id.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_retrieve(struct pri *ctrl, q931_call *call, int channel);
/*!
* \brief Send the RETRIEVE ACKNOWLEDGE message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param channel Encoded channel id to use.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel);
/*!
* \brief Send the RETRIEVE REJECT message.
*
* \param ctrl D channel controller.
* \param call Q.931 call leg
* \param cause Q.931 cause code for rejecting the retrieve request.
*
* \retval 0 on success.
* \retval -1 on error.
*/
int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause);
/* Get/Set PRI Timers */ /* Get/Set PRI Timers */
#define PRI_GETSET_TIMERS #define PRI_GETSET_TIMERS
int pri_set_timer(struct pri *pri, int timer, int value); int pri_set_timer(struct pri *pri, int timer, int value);
@ -1015,6 +1285,9 @@ enum PRI_TIMERS_AND_COUNTERS {
PRI_TIMER_TM20, /*!< Maximum time awaiting XID response */ PRI_TIMER_TM20, /*!< Maximum time awaiting XID response */
PRI_TIMER_NM20, /*!< Number of XID retransmits */ PRI_TIMER_NM20, /*!< Number of XID retransmits */
PRI_TIMER_T_HOLD, /*!< Maximum time to wait for HOLD request response. */
PRI_TIMER_T_RETRIEVE, /*!< Maximum time to wait for RETRIEVE request response. */
/* Must be last in the enum list */ /* Must be last in the enum list */
_PRI_MAX_TIMERS, _PRI_MAX_TIMERS,
PRI_MAX_TIMERS = (_PRI_MAX_TIMERS < 32) ? 32 : _PRI_MAX_TIMERS PRI_MAX_TIMERS = (_PRI_MAX_TIMERS < 32) ? 32 : _PRI_MAX_TIMERS

178
pri.c
View File

@ -86,6 +86,8 @@ static const struct pri_timer_table pri_timer[] = {
{ "T320", PRI_TIMER_T320, PRI_ALL_SWITCHES }, { "T320", PRI_TIMER_T320, PRI_ALL_SWITCHES },
{ "T321", PRI_TIMER_T321, PRI_ALL_SWITCHES }, { "T321", PRI_TIMER_T321, PRI_ALL_SWITCHES },
{ "T322", PRI_TIMER_T322, PRI_ALL_SWITCHES }, { "T322", PRI_TIMER_T322, PRI_ALL_SWITCHES },
{ "T-HOLD", PRI_TIMER_T_HOLD, PRI_ALL_SWITCHES },
{ "T-RETRIEVE", PRI_TIMER_T_RETRIEVE, PRI_ALL_SWITCHES },
/* *INDENT-ON* */ /* *INDENT-ON* */
}; };
@ -150,6 +152,10 @@ static void pri_default_timers(struct pri *ctrl, int switchtype)
ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */ ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */
ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */ ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */
ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */ ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */
ctrl->timers[PRI_TIMER_T303] = 4 * 1000; /* Length between SETUP retransmissions and timeout */
ctrl->timers[PRI_TIMER_T_HOLD] = 4 * 1000; /* Wait for HOLD request response. */
ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */
/* Set any switch specific override default values */ /* Set any switch specific override default values */
switch (switchtype) { switch (switchtype) {
@ -381,6 +387,12 @@ char *pri_event2str(int id)
{ PRI_EVENT_KEYPAD_DIGIT, "Keypad Digit" }, { PRI_EVENT_KEYPAD_DIGIT, "Keypad Digit" },
{ PRI_EVENT_SERVICE, "Service" }, { PRI_EVENT_SERVICE, "Service" },
{ PRI_EVENT_SERVICE_ACK, "Service ACK" }, { PRI_EVENT_SERVICE_ACK, "Service ACK" },
{ PRI_EVENT_HOLD, "Hold" },
{ PRI_EVENT_HOLD_ACK, "Hold Ack" },
{ PRI_EVENT_HOLD_REJ, "Hold Rej" },
{ PRI_EVENT_RETRIEVE, "Retrieve" },
{ PRI_EVENT_RETRIEVE_ACK, "Retrieve ACK" },
{ PRI_EVENT_RETRIEVE_REJ, "Retrieve Rej" },
/* *INDENT-ON* */ /* *INDENT-ON* */
}; };
@ -539,15 +551,6 @@ int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits)
return q931_keypad_facility(pri, call, digits); return q931_keypad_facility(pri, call, digits);
} }
int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason)
{
if (!pri || !call)
return -1;
return qsig_cf_callrerouting(pri, call, dest, original, reason);
}
int pri_notify(struct pri *pri, q931_call *call, int channel, int info) int pri_notify(struct pri *pri, q931_call *call, int channel, int info)
{ {
if (!pri || !call) if (!pri || !call)
@ -558,7 +561,7 @@ int pri_notify(struct pri *pri, q931_call *call, int channel, int info)
void pri_destroycall(struct pri *pri, q931_call *call) void pri_destroycall(struct pri *pri, q931_call *call)
{ {
if (pri && call) if (pri && call)
__q931_destroycall(pri, call); q931_destroycall(pri, call);
return; return;
} }
@ -669,6 +672,8 @@ static void pri_copy_party_id_to_q931(struct q931_party_id *q931_id, const struc
int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected) int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected)
{ {
struct q931_party_id party_id; struct q931_party_id party_id;
unsigned idx;
struct q931_call *subcall;
if (!ctrl || !call) { if (!ctrl || !call) {
return -1; return -1;
@ -682,6 +687,16 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr
} }
call->local_id = party_id; call->local_id = party_id;
/* Update all subcalls with new local_id. */
if (call->outboundbroadcast && call->master_call == call) {
for (idx = 0; idx < Q931_MAX_TEI; ++idx) {
subcall = call->subcalls[idx];
if (subcall) {
subcall->local_id = party_id;
}
}
}
switch (call->ourcallstate) { switch (call->ourcallstate) {
case Q931_CALL_STATE_CALL_INITIATED: case Q931_CALL_STATE_CALL_INITIATED:
case Q931_CALL_STATE_OVERLAP_SENDING: case Q931_CALL_STATE_OVERLAP_SENDING:
@ -723,6 +738,9 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr
int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting) int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting)
{ {
unsigned idx;
struct q931_call *subcall;
if (!ctrl || !call) { if (!ctrl || !call) {
return -1; return -1;
} }
@ -732,6 +750,21 @@ int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_p
q931_party_id_fixup(ctrl, &call->redirecting.to); q931_party_id_fixup(ctrl, &call->redirecting.to);
call->redirecting.reason = redirecting->reason; call->redirecting.reason = redirecting->reason;
/*
* Update all subcalls with new redirecting.to information and reason.
* I do not think we will ever have any subcalls when this data is relevant,
* but update it just in case.
*/
if (call->outboundbroadcast && call->master_call == call) {
for (idx = 0; idx < Q931_MAX_TEI; ++idx) {
subcall = call->subcalls[idx];
if (subcall) {
subcall->redirecting.to = call->redirecting.to;
subcall->redirecting.reason = redirecting->reason;
}
}
}
switch (call->ourcallstate) { switch (call->ourcallstate) {
case Q931_CALL_STATE_NULL: case Q931_CALL_STATE_NULL:
/* Save the remaining redirecting information before we place a call. */ /* Save the remaining redirecting information before we place a call. */
@ -1046,7 +1079,7 @@ void pri_message(struct pri *pri, char *fmt, ...)
vsnprintf(tmp, sizeof(tmp), fmt, ap); vsnprintf(tmp, sizeof(tmp), fmt, ap);
va_end(ap); va_end(ap);
if (__pri_message) if (__pri_message)
__pri_message(pri, tmp); __pri_message(PRI_MASTER(pri), tmp);
else else
fputs(tmp, stdout); fputs(tmp, stdout);
} }
@ -1059,7 +1092,7 @@ void pri_error(struct pri *pri, char *fmt, ...)
vsnprintf(tmp, sizeof(tmp), fmt, ap); vsnprintf(tmp, sizeof(tmp), fmt, ap);
va_end(ap); va_end(ap);
if (__pri_error) if (__pri_error)
__pri_error(pri, tmp); __pri_error(PRI_MASTER(pri), tmp);
else else
fputs(tmp, stderr); fputs(tmp, stderr);
} }
@ -1320,3 +1353,124 @@ void pri_sr_set_reversecharge(struct pri_sr *sr, int requested)
{ {
sr->reversecharge = requested; sr->reversecharge = requested;
} }
void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits)
{
sr->keypad_digits = keypad_digits;
}
void pri_hold_enable(struct pri *ctrl, int enable)
{
ctrl = PRI_MASTER(ctrl);
if (ctrl) {
ctrl->hold_support = enable ? 1 : 0;
}
}
int pri_hold(struct pri *ctrl, q931_call *call)
{
if (!ctrl || !call) {
return -1;
}
return q931_send_hold(ctrl, call);
}
int pri_hold_ack(struct pri *ctrl, q931_call *call)
{
if (!ctrl || !call) {
return -1;
}
return q931_send_hold_ack(ctrl, call);
}
int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause)
{
if (!ctrl || !call) {
return -1;
}
return q931_send_hold_rej(ctrl, call, cause);
}
int pri_retrieve(struct pri *ctrl, q931_call *call, int channel)
{
if (!ctrl || !call) {
return -1;
}
return q931_send_retrieve(ctrl, call, channel);
}
int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel)
{
if (!ctrl || !call) {
return -1;
}
return q931_send_retrieve_ack(ctrl, call, channel);
}
int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause)
{
if (!ctrl || !call) {
return -1;
}
return q931_send_retrieve_rej(ctrl, call, cause);
}
int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason)
{
if (!pri || !call || !dest)
return -1;
return qsig_cf_callrerouting(pri, call, dest, original, reason);
}
void pri_reroute_enable(struct pri *ctrl, int enable)
{
ctrl = PRI_MASTER(ctrl);
if (ctrl) {
ctrl->deflection_support = enable ? 1 : 0;
}
}
int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option)
{
const struct q931_party_id *caller_id;
struct q931_party_id local_caller;
struct q931_party_redirecting reroute;
if (!ctrl || !call || !deflection) {
return -1;
}
if (caller) {
/* Convert the caller update information. */
pri_copy_party_id_to_q931(&local_caller, caller);
q931_party_id_fixup(ctrl, &local_caller);
caller_id = &local_caller;
} else {
caller_id = NULL;
}
/* Convert the deflection information. */
q931_party_redirecting_init(&reroute);
pri_copy_party_id_to_q931(&reroute.from, &deflection->from);
q931_party_id_fixup(ctrl, &reroute.from);
pri_copy_party_id_to_q931(&reroute.to, &deflection->to);
q931_party_id_fixup(ctrl, &reroute.to);
pri_copy_party_id_to_q931(&reroute.orig_called, &deflection->orig_called);
q931_party_id_fixup(ctrl, &reroute.orig_called);
reroute.reason = deflection->reason;
reroute.orig_reason = deflection->orig_reason;
if (deflection->count <= 0) {
/*
* We are deflecting with an unknown count
* so assume the count is one.
*/
reroute.count = 1;
} else if (deflection->count < PRI_MAX_REDIRECTS) {
reroute.count = deflection->count;
} else {
reroute.count = PRI_MAX_REDIRECTS;
}
return send_reroute_request(ctrl, call, caller_id, &reroute, subscription_option);
}

File diff suppressed because it is too large Load Diff

View File

@ -74,6 +74,7 @@ int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2);
int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2);
int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason); int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason);
int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option);
/* starts a QSIG Path Replacement */ /* starts a QSIG Path Replacement */
int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2);
@ -106,9 +107,9 @@ struct rose_msg_result;
struct rose_msg_error; struct rose_msg_error;
struct rose_msg_reject; struct rose_msg_reject;
void rose_handle_invoke(struct pri *ctrl, q931_call *call, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_invoke *invoke); void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_invoke *invoke);
void rose_handle_result(struct pri *ctrl, q931_call *call, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_result *result); void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_result *result);
void rose_handle_error(struct pri *ctrl, q931_call *call, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_error *error); void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_error *error);
void rose_handle_reject(struct pri *ctrl, q931_call *call, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *reject); void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *reject);
#endif /* _PRI_FACILITY_H */ #endif /* _PRI_FACILITY_H */

View File

@ -70,6 +70,8 @@ struct pri {
int protodisc; int protodisc;
unsigned int bri:1; unsigned int bri:1;
unsigned int acceptinbanddisconnect:1; /* Should we allow inband progress after DISCONNECT? */ unsigned int acceptinbanddisconnect:1; /* Should we allow inband progress after DISCONNECT? */
unsigned int hold_support:1;/* TRUE if upper layer supports call hold. */
unsigned int deflection_support:1;/* TRUE if upper layer supports call deflection/rerouting. */
/* Q.921 State */ /* Q.921 State */
int q921_state; int q921_state;
@ -287,6 +289,7 @@ struct pri_sr {
int cis_call; int cis_call;
int cis_auto_disconnect; int cis_auto_disconnect;
const char *useruserinfo; const char *useruserinfo;
const char *keypad_digits;
int transferable; int transferable;
int reversecharge; int reversecharge;
}; };
@ -295,6 +298,8 @@ struct pri_sr {
#define PRI_SWITCH_GR303_EOC_PATH 19 #define PRI_SWITCH_GR303_EOC_PATH 19
#define PRI_SWITCH_GR303_TMC_SWITCHING 20 #define PRI_SWITCH_GR303_TMC_SWITCHING 20
#define Q931_MAX_TEI 8
struct apdu_event { struct apdu_event {
int message; /* What message to send the ADPU in */ int message; /* What message to send the ADPU in */
void (*callback)(void *data); /* Callback function for when response is received */ void (*callback)(void *data); /* Callback function for when response is received */
@ -327,6 +332,22 @@ enum INCOMING_CT_STATE {
INCOMING_CT_STATE_POST_CONNECTED_LINE INCOMING_CT_STATE_POST_CONNECTED_LINE
}; };
/*! Call hold supplementary states. */
enum Q931_HOLD_STATE {
/*! \brief No call hold activity. */
Q931_HOLD_STATE_IDLE,
/*! \brief Request made to hold call. */
Q931_HOLD_STATE_HOLD_REQ,
/*! \brief Request received to hold call. */
Q931_HOLD_STATE_HOLD_IND,
/*! \brief Call is held. */
Q931_HOLD_STATE_CALL_HELD,
/*! \brief Request made to retrieve call. */
Q931_HOLD_STATE_RETRIEVE_REQ,
/*! \brief Request received to retrieve call. */
Q931_HOLD_STATE_RETRIEVE_IND,
};
/* q931_call datastructure */ /* q931_call datastructure */
struct q931_call { struct q931_call {
struct pri *pri; /* PRI */ struct pri *pri; /* PRI */
@ -442,6 +463,12 @@ struct q931_call {
/*! \brief Incoming call transfer state. */ /*! \brief Incoming call transfer state. */
enum INCOMING_CT_STATE incoming_ct_state; enum INCOMING_CT_STATE incoming_ct_state;
/*! Call hold supplementary state. */
enum Q931_HOLD_STATE hold_state;
/*! Call hold event timer */
int hold_timer;
int deflection_in_progress; /*!< CallDeflection for NT PTMP in progress. */
int useruserprotocoldisc; int useruserprotocoldisc;
char useruserinfo[256]; char useruserinfo[256];
@ -462,6 +489,22 @@ struct q931_call {
-1 - No reverse charging -1 - No reverse charging
1 - Reverse charging 1 - Reverse charging
0,2-7 - Reserved for future use */ 0,2-7 - Reserved for future use */
int t303_timer;
int t303_expirycnt;
int hangupinitiated;
/*! \brief TRUE if we broadcast this call's SETUP message. */
int outboundbroadcast;
int performing_fake_clearing;
/*!
* \brief Master call controlling this call.
* \note Always valid. Master and normal calls point to self.
*/
struct q931_call *master_call;
/* These valid in master call only */
struct q931_call *subcalls[Q931_MAX_TEI];
int pri_winner;
}; };
extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data);
@ -484,6 +527,11 @@ void __pri_free_tei(struct pri *p);
void q931_party_name_init(struct q931_party_name *name); void q931_party_name_init(struct q931_party_name *name);
void q931_party_number_init(struct q931_party_number *number); void q931_party_number_init(struct q931_party_number *number);
void q931_party_subaddress_init(struct q931_party_subaddress *subaddr); void q931_party_subaddress_init(struct q931_party_subaddress *subaddr);
#define q931_party_address_to_id(q931_id, q931_address) \
do { \
(q931_id)->number = (q931_address)->number; \
/*(q931_id)->subaddress = (q931_address)->subaddress;*/ \
} while (0)
void q931_party_address_init(struct q931_party_address *address); void q931_party_address_init(struct q931_party_address *address);
void q931_party_id_init(struct q931_party_id *id); void q931_party_id_init(struct q931_party_id *id);
void q931_party_redirecting_init(struct q931_party_redirecting *redirecting); void q931_party_redirecting_init(struct q931_party_redirecting *redirecting);
@ -505,8 +553,40 @@ int q931_party_id_presentation(const struct q931_party_id *id);
const char *q931_call_state_str(enum Q931_CALL_STATE callstate); const char *q931_call_state_str(enum Q931_CALL_STATE callstate);
int q931_is_ptmp(const struct pri *ctrl); int q931_is_ptmp(const struct pri *ctrl);
int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type);
struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl);
int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number); int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number);
static inline struct pri * PRI_MASTER(struct pri *mypri)
{
struct pri *pri = mypri;
if (!pri)
return NULL;
while (pri->master)
pri = pri->master;
return pri;
}
static inline int BRI_NT_PTMP(struct pri *mypri)
{
struct pri *pri;
pri = PRI_MASTER(mypri);
return pri->bri && (((pri)->localtype == PRI_NETWORK) && ((pri)->tei == Q921_TEI_GROUP));
}
static inline int BRI_TE_PTMP(struct pri *mypri)
{
struct pri *pri;
pri = PRI_MASTER(mypri);
return pri->bri && (((pri)->localtype == PRI_CPE) && ((pri)->tei == Q921_TEI_GROUP));
}
#endif #endif

View File

@ -192,6 +192,8 @@ extern pri_event *q921_receive(struct pri *pri, q921_h *h, int len);
extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr); extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr);
extern int q921_transmit_uiframe(struct pri *pri, void *buf, int len);
extern pri_event *q921_dchannel_up(struct pri *pri); extern pri_event *q921_dchannel_up(struct pri *pri);
extern pri_event *q921_dchannel_down(struct pri *pri); extern pri_event *q921_dchannel_down(struct pri *pri);

View File

@ -424,6 +424,22 @@ enum Q931_CALL_STATE {
Q931_CALL_STATE_NOT_SET = 0xFF, Q931_CALL_STATE_NOT_SET = 0xFF,
}; };
/*! Q.931 call establishment state ranking for competing calls in PTMP NT mode. */
enum Q931_RANKED_CALL_STATE {
/*! Call is present but has no response yet. */
Q931_RANKED_CALL_STATE_PRESENT,
/*! Call is collecting digits. */
Q931_RANKED_CALL_STATE_OVERLAP,
/*! Call routing is happening. */
Q931_RANKED_CALL_STATE_PROCEEDING,
/*! Called party is being alerted of the call. */
Q931_RANKED_CALL_STATE_ALERTING,
/*! Call is connected. A winner has been declared. */
Q931_RANKED_CALL_STATE_CONNECT,
/*! Call is in some non-call establishment state (likely disconnecting). */
Q931_RANKED_CALL_STATE_OTHER,
};
/* EuroISDN */ /* EuroISDN */
#define Q931_SENDING_COMPLETE 0xa1 #define Q931_SENDING_COMPLETE 0xa1
@ -474,8 +490,16 @@ extern q931_call *q931_new_call(struct pri *pri);
extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req); extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req);
extern void q931_dump(struct pri *pri, q931_h *h, int len, int txrx); extern void q931_dump(struct pri *pri, q931_h *h, int len, int txrx);
extern void __q931_destroycall(struct pri *pri, q931_call *c); void q931_destroycall(struct pri *pri, q931_call *c);
extern void q931_dl_indication(struct pri *pri, int event); extern void q931_dl_indication(struct pri *pri, int event);
int q931_send_hold(struct pri *ctrl, struct q931_call *call);
int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call);
int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause);
int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel);
int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel);
int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause);
#endif #endif

128
q921.c
View File

@ -27,6 +27,7 @@
* terms granted here. * terms granted here.
*/ */
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -477,6 +478,45 @@ static void t200_expire(void *vpri)
} }
} }
int q921_transmit_uiframe(struct pri *pri, void *buf, int len)
{
uint8_t ubuf[512];
q921_h *h = (void *)&ubuf[0];
if (len >= 512) {
pri_error(pri, "Requested to send UI frame larger than 512 bytes!\n");
return -1;
}
memset(ubuf, 0, sizeof(ubuf));
h->h.sapi = 0;
h->h.ea1 = 0;
h->h.ea2 = 1;
h->h.tei = pri->tei;
h->u.m3 = 0;
h->u.m2 = 0;
h->u.p_f = 0; /* Poll bit set */
h->u.ft = Q921_FRAMETYPE_U;
switch(pri->localtype) {
case PRI_NETWORK:
h->h.c_r = 1;
break;
case PRI_CPE:
h->h.c_r = 0;
break;
default:
pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype);
return -1;
}
memcpy(h->u.data, buf, len);
q921_transmit(pri, h, len + 3);
return 0;
}
int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr) int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr)
{ {
q921_frame *f, *prev=NULL; q921_frame *f, *prev=NULL;
@ -515,6 +555,10 @@ int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr)
pri->txqueue = f; pri->txqueue = f;
/* Immediately transmit unless we're in a recovery state, or the window /* Immediately transmit unless we're in a recovery state, or the window
size is too big */ size is too big */
if (pri->debug & PRI_DEBUG_Q921_DUMP) {
pri_message(pri, "TEI/SAPI: %d/%d state %d retran %d busy %d\n",
pri->tei, pri->sapi, pri->q921_state, pri->retrans, pri->busy);
}
if ((pri->q921_state == Q921_LINK_CONNECTION_ESTABLISHED) && (!pri->retrans && !pri->busy)) { if ((pri->q921_state == Q921_LINK_CONNECTION_ESTABLISHED) && (!pri->retrans && !pri->busy)) {
if (pri->windowlen < pri->window) { if (pri->windowlen < pri->window) {
q921_send_queued_iframes(pri); q921_send_queued_iframes(pri);
@ -558,11 +602,15 @@ static void t203_expire(void *vpri)
/* Start timer T200 to resend our RR if we don't get it */ /* Start timer T200 to resend our RR if we don't get it */
pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri);
} else { } else {
if (pri->debug & PRI_DEBUG_Q921_DUMP) if (pri->debug & PRI_DEBUG_Q921_DUMP) {
pri_message(pri, "T203 counter expired in weird state %d\n", pri->q921_state); pri_message(pri,
"T203 counter expired in weird state %d on pri with SAPI/TEI of %d/%d\n",
pri->q921_state, pri->sapi, pri->tei);
}
pri->t203_timer = 0; pri->t203_timer = 0;
} }
} }
static pri_event *q921_handle_iframe(struct pri *pri, q921_i *i, int len) static pri_event *q921_handle_iframe(struct pri *pri, q921_i *i, int len)
{ {
int res; int res;
@ -879,8 +927,14 @@ static void q921_tei_release_and_reacquire(struct pri *master)
static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len)
{ {
int ri; int ri;
struct pri *sub; struct pri *sub = pri;
int tei; int tei;
if (!BRI_NT_PTMP(pri) && !BRI_TE_PTMP(pri)) {
pri_error(pri, "Received MDL/TEI managemement message, but configured for mode other than PTMP!\n");
return NULL;
}
if (pri->debug & PRI_DEBUG_Q921_STATE) if (pri->debug & PRI_DEBUG_Q921_STATE)
pri_message(pri, "Received MDL message\n"); pri_message(pri, "Received MDL message\n");
if (h->data[0] != 0x0f) { if (h->data[0] != 0x0f) {
@ -895,17 +949,19 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len)
tei = (h->data[4] >> 1); tei = (h->data[4] >> 1);
switch(h->data[3]) { switch(h->data[3]) {
case Q921_TEI_IDENTITY_REQUEST: case Q921_TEI_IDENTITY_REQUEST:
if (!BRI_NT_PTMP(pri)) {
return NULL;
}
if (tei != 127) { if (tei != 127) {
pri_error(pri, "Received TEI identity request with invalid TEI %d\n", tei); pri_error(pri, "Received TEI identity request with invalid TEI %d\n", tei);
q921_send_tei(pri, Q921_TEI_IDENTITY_DENIED, ri, tei, 1); q921_send_tei(pri, Q921_TEI_IDENTITY_DENIED, ri, tei, 1);
} }
/* Go to master */
for (sub = pri; sub->master; sub = sub->master);
tei = 64; tei = 64;
/*! \todo XXX Error: The following loop never terminates! */ while (sub->subchannel) {
while(sub->subchannel) { if (sub->subchannel->tei == tei)
if(sub->subchannel->tei == tei)
++tei; ++tei;
sub = sub->subchannel;
} }
sub->subchannel = __pri_new_tei(-1, pri->localtype, pri->switchtype, pri, NULL, NULL, NULL, tei, 1); sub->subchannel = __pri_new_tei(-1, pri->localtype, pri->switchtype, pri, NULL, NULL, NULL, tei, 1);
if (!sub->subchannel) { if (!sub->subchannel) {
@ -915,6 +971,9 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len)
q921_send_tei(pri, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1); q921_send_tei(pri, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1);
break; break;
case Q921_TEI_IDENTITY_ASSIGNED: case Q921_TEI_IDENTITY_ASSIGNED:
if (!BRI_TE_PTMP(pri))
return NULL;
if (ri != pri->ri) { if (ri != pri->ri) {
pri_message(pri, "TEI assignment received for invalid Ri %02x (our is %02x)\n", ri, pri->ri); pri_message(pri, "TEI assignment received for invalid Ri %02x (our is %02x)\n", ri, pri->ri);
return NULL; return NULL;
@ -936,6 +995,8 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len)
pri->q921_state = Q921_TEI_ASSIGNED; pri->q921_state = Q921_TEI_ASSIGNED;
break; break;
case Q921_TEI_IDENTITY_CHECK_REQUEST: case Q921_TEI_IDENTITY_CHECK_REQUEST:
if (!BRI_TE_PTMP(pri))
return NULL;
/* We're assuming one TEI per PRI in TE PTMP mode */ /* We're assuming one TEI per PRI in TE PTMP mode */
/* If no subchannel (TEI) ignore */ /* If no subchannel (TEI) ignore */
@ -948,6 +1009,8 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len)
break; break;
case Q921_TEI_IDENTITY_REMOVE: case Q921_TEI_IDENTITY_REMOVE:
if (!BRI_TE_PTMP(pri))
return NULL;
/* XXX: Assuming multiframe mode has been disconnected already */ /* XXX: Assuming multiframe mode has been disconnected already */
if (!pri->subchannel) if (!pri->subchannel)
return NULL; return NULL;
@ -989,6 +1052,10 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len)
pri_error(pri, "!! Received short I-frame (expected 4, got %d)\n", len); pri_error(pri, "!! Received short I-frame (expected 4, got %d)\n", len);
break; break;
} }
/* T203 is rescheduled only on reception of I frames or S frames */
reschedule_t203(pri);
return q921_handle_iframe(pri, &h->i, len); return q921_handle_iframe(pri, &h->i, len);
break; break;
case 1: case 1:
@ -1000,6 +1067,10 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len)
pri_error(pri, "!! Received short S-frame (expected 4, got %d)\n", len); pri_error(pri, "!! Received short S-frame (expected 4, got %d)\n", len);
break; break;
} }
/* T203 is rescheduled only on reception of I frames or S frames */
reschedule_t203(pri);
switch(h->s.ss) { switch(h->s.ss) {
case 0: case 0:
/* Receiver Ready */ /* Receiver Ready */
@ -1149,7 +1220,6 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len)
/* Acknowledge */ /* Acknowledge */
q921_send_ua(pri, h->u.p_f); q921_send_ua(pri, h->u.p_f);
ev = q921_dchannel_down(pri); ev = q921_dchannel_down(pri);
q921_restart(pri, 0);
return ev; return ev;
case 3: case 3:
if (h->u.m2 == 3) { if (h->u.m2 == 3) {
@ -1209,6 +1279,34 @@ static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len)
return NULL; return NULL;
} }
static pri_event *q921_handle_unmatched_frame(struct pri *pri, q921_h *h, int len)
{
pri = PRI_MASTER(pri);
if (h->h.tei < 64) {
pri_error(pri, "Do not support manual TEI range. Discarding\n");
return NULL;
}
if (h->h.sapi != Q921_SAPI_CALL_CTRL) {
pri_error(pri, "Message with SAPI other than CALL CTRL is discarded\n");
return NULL;
}
if (pri->debug & PRI_DEBUG_Q921_DUMP) {
pri_message(pri,
"Could not find candidate subchannel for received frame with SAPI/TEI of %d/%d.\n",
h->h.sapi, h->h.tei);
pri_message(pri, "Sending TEI release, in order to re-establish TEI state\n");
}
/* Q.921 says we should send the remove message twice, in case of link corruption */
q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1);
q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1);
return NULL;
}
static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len) static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len)
{ {
pri_event *ev; pri_event *ev;
@ -1222,26 +1320,22 @@ static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len)
if (h->h.ea1 || !(h->h.ea2)) if (h->h.ea1 || !(h->h.ea2))
return NULL; return NULL;
#if 0 /* Will be rejected by subchannel analyzis */
/* Check for broadcasts - not yet handled */
if (h->h.tei == Q921_TEI_GROUP)
return NULL;
#endif
if (!((h->h.sapi == pri->sapi) && ((h->h.tei == pri->tei) || (h->h.tei == Q921_TEI_GROUP)))) { if (!((h->h.sapi == pri->sapi) && ((h->h.tei == pri->tei) || (h->h.tei == Q921_TEI_GROUP)))) {
/* Check for SAPIs we don't yet handle */ /* Check for SAPIs we don't yet handle */
/* If it's not us, try any subchannels we have */ /* If it's not us, try any subchannels we have */
if (pri->subchannel) if (pri->subchannel)
return q921_receive(pri->subchannel, h, len + 2); return q921_receive(pri->subchannel, h, len + 2);
else { else {
return NULL; /* This means we couldn't find a candidate subchannel for it...
* Time for some corrective action */
return q921_handle_unmatched_frame(pri, h, len);
} }
} }
if (pri->debug & PRI_DEBUG_Q921_DUMP) if (pri->debug & PRI_DEBUG_Q921_DUMP)
pri_message(pri, "Handling message for SAPI/TEI=%d/%d\n", h->h.sapi, h->h.tei); pri_message(pri, "Handling message for SAPI/TEI=%d/%d\n", h->h.sapi, h->h.tei);
ev = __q921_receive_qualified(pri, h, len); ev = __q921_receive_qualified(pri, h, len);
reschedule_t203(pri);
return ev; return ev;
} }

1897
q931.c

File diff suppressed because it is too large Load Diff