Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 287321 Details for
Bug 383987
net-misc/asterisk-1.8.7.0_rc2: Updated patches for new release of asterisk
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
t38gateway-1.8.patch T.30-T.38 gateway backported to 1.8 from 10
13-t38gateway-1.8.patch (text/plain), 120.44 KB, created by
Erik Wallin
on 2011-09-21 17:11:00 UTC
(
hide
)
Description:
t38gateway-1.8.patch T.30-T.38 gateway backported to 1.8 from 10
Filename:
MIME Type:
Creator:
Erik Wallin
Created:
2011-09-21 17:11:00 UTC
Size:
120.44 KB
patch
obsolete
>Index: channels/chan_sip.c >=================================================================== >--- channels/chan_sip.c (.../branches/1.8) (revision 336160) >+++ channels/chan_sip.c (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -4316,6 +4316,9 @@ > case T38_ENABLED: > state = T38_STATE_NEGOTIATED; > break; >+ case T38_REJECTED: >+ state = T38_STATE_REJECTED; >+ break; > default: > state = T38_STATE_UNKNOWN; > } >@@ -4980,6 +4983,7 @@ > parameters.request_response = AST_T38_NEGOTIATED; > ast_udptl_set_tag(p->udptl, "SIP/%s", p->username); > break; >+ case T38_REJECTED: > case T38_DISABLED: > if (old == T38_ENABLED) { > parameters.request_response = AST_T38_TERMINATED; >@@ -6558,11 +6562,11 @@ > case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */ > /* Negotiation can not take place without a valid max_ifp value. */ > if (!parameters->max_ifp) { >- change_t38_state(p, T38_DISABLED); > if (p->t38.state == T38_PEER_REINVITE) { > AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr")); > transmit_response_reliable(p, "488 Not acceptable here", &p->initreq); > } >+ change_t38_state(p, T38_REJECTED); > break; > } else if (p->t38.state == T38_PEER_REINVITE) { > AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr")); >@@ -6600,7 +6604,7 @@ > case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */ > if (p->t38.state == T38_PEER_REINVITE) { > AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr")); >- change_t38_state(p, T38_DISABLED); >+ change_t38_state(p, T38_REJECTED); > transmit_response_reliable(p, "488 Not acceptable here", &p->initreq); > } else if (p->t38.state == T38_ENABLED) > transmit_reinvite_with_sdp(p, FALSE, FALSE); >@@ -9051,7 +9055,7 @@ > } > } > >- if ((portno == -1) && (p->t38.state != T38_DISABLED)) { >+ if ((portno == -1) && (p->t38.state != T38_DISABLED) && (p->t38.state != T38_REJECTED)) { > ast_debug(3, "Have T.38 but no audio, accepting offer anyway\n"); > return 0; > } >@@ -18980,7 +18984,7 @@ > } else if (!strcasecmp(data, "peername")) { > ast_copy_string(buf, p->peername, len); > } else if (!strcasecmp(data, "t38passthrough")) { >- if (p->t38.state == T38_DISABLED) { >+ if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) { > ast_copy_string(buf, "0", len); > } else { /* T38 is offered or enabled in this call */ > ast_copy_string(buf, "1", len); >@@ -19746,7 +19750,7 @@ > case 606: /* Not Acceptable */ > xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); > if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { >- change_t38_state(p, T38_DISABLED); >+ change_t38_state(p, T38_REJECTED); > /* Try to reset RTP timers */ > //ast_rtp_set_rtptimers_onhold(p->rtp); > >@@ -21554,7 +21558,7 @@ > * want to abort the negotiation process > */ > if (p->t38id != -1) { >- change_t38_state(p, T38_DISABLED); >+ change_t38_state(p, T38_REJECTED); > transmit_response_reliable(p, "488 Not acceptable here", &p->initreq); > p->t38id = -1; > dialog_unref(p, "unref the dialog ptr from sip_t38_abort, because it held a dialog ptr"); >@@ -22429,7 +22433,7 @@ > } else if (p->t38.state == T38_ENABLED) { > ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); > transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL))); >- } else if (p->t38.state == T38_DISABLED) { >+ } else if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) { > /* If this is not a re-invite or something to ignore - it's critical */ > if (p->srtp && !ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)) { > ast_log(LOG_WARNING, "Target does not support required crypto\n"); >Index: channels/sip/include/sip.h >=================================================================== >--- channels/sip/include/sip.h (.../branches/1.8) (revision 336160) >+++ channels/sip/include/sip.h (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -597,7 +597,8 @@ > T38_DISABLED = 0, /*!< Not enabled */ > T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ > T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ >- T38_ENABLED /*!< Negotiated (enabled) */ >+ T38_ENABLED, /*!< Negotiated (enabled) */ >+ T38_REJECTED /*!< Refused */ > }; > > /*! \brief Parameters to know status of transfer */ >Index: addons/chan_ooh323.c >=================================================================== >--- addons/chan_ooh323.c (.../branches/1.8) (revision 336160) >+++ addons/chan_ooh323.c (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -25,6 +25,42 @@ > #include "chan_ooh323.h" > #include <math.h> > >+/*** DOCUMENTATION >+ <function name="OOH323" language="en_US"> >+ <synopsis> >+ Allow Setting / Reading OOH323 Settings >+ </synopsis> >+ <syntax> >+ <parameter name="name" required="true"> >+ <enumlist> >+ <enum name="faxdetect"> >+ <para>Fax Detect [R/W]</para> >+ <para>Returns 0 or 1</para> >+ <para>Write yes or no</para> >+ </enum> >+ </enumlist> >+ <enumlist> >+ <enum name="t38support"> >+ <para>t38support [R/W]</para> >+ <para>Returns 0 or 1</para> >+ <para>Write yes or no</para> >+ </enum> >+ </enumlist> >+ <enumlist> >+ <enum name="h323id"> >+ <para>Returns h323id [R]</para> >+ </enum> >+ </enumlist> >+ </parameter> >+ </syntax> >+ <description> >+ <para>Read and set channel parameters in the dialplan. >+ <replaceable>name</replaceable> is one of the above only those with a [W] can be writen to. >+ </para> >+ </description> >+ </function> >+***/ >+ > #define FORMAT_STRING_SIZE 512 > > /* Defaults */ >@@ -50,6 +86,9 @@ > #define T38_ENABLED 1 > #define T38_FAXGW 1 > >+#define FAXDETECT_CNG 1 >+#define FAXDETECT_T38 2 >+ > /* Channel description */ > static const char type[] = "OOH323"; > static const char tdesc[] = "Objective Systems H323 Channel Driver"; >@@ -140,6 +179,8 @@ > struct ast_rtp_instance *vrtp; /* Placeholder for now */ > > int t38support; /* T.38 mode - disable, transparent, faxgw */ >+ int faxdetect; >+ int faxdetected; > int rtptimeout; > struct ast_udptl *udptl; > int faxmode; >@@ -201,25 +242,26 @@ > /* Profile of H.323 user registered with PBX*/ > struct ooh323_user{ > ast_mutex_t lock; >- char name[256]; >- char context[AST_MAX_EXTENSION]; >- int incominglimit; >- unsigned inUse; >- char accountcode[20]; >- int amaflags; >- format_t capability; >+ char name[256]; >+ char context[AST_MAX_EXTENSION]; >+ int incominglimit; >+ unsigned inUse; >+ char accountcode[20]; >+ int amaflags; >+ format_t capability; > struct ast_codec_pref prefs; >- int dtmfmode; >- int dtmfcodec; >- int t38support; >- int rtptimeout; >- int mUseIP; /* Use IP address or H323-ID to search user */ >- char mIP[20]; >- struct OOH323Regex *rtpmask; >- char rtpmaskstr[120]; >- int rtdrcount, rtdrinterval; >- int faststart, h245tunneling; >- int g729onlyA; >+ int dtmfmode; >+ int dtmfcodec; >+ int faxdetect; >+ int t38support; >+ int rtptimeout; >+ int mUseIP; /* Use IP address or H323-ID to search user */ >+ char mIP[20]; >+ struct OOH323Regex *rtpmask; >+ char rtpmaskstr[120]; >+ int rtdrcount, rtdrinterval; >+ int faststart, h245tunneling; >+ int g729onlyA; > struct ooh323_user *next; > }; > >@@ -235,6 +277,7 @@ > int amaflags; > int dtmfmode; > int dtmfcodec; >+ int faxdetect; > int t38support; > int mFriend; /* indicates defined as friend */ > char ip[20]; >@@ -295,6 +338,7 @@ > static struct ast_codec_pref gPrefs; > static int gDTMFMode = H323_DTMF_RFC2833; > static int gDTMFCodec = 101; >+static int gFAXdetect = FAXDETECT_CNG; > static int gT38Support = T38_FAXGW; > static char gGatekeeper[100]; > static enum RasGatekeeperMode gRasGkMode = RasNoGatekeeper; >@@ -343,10 +387,12 @@ > > > static struct ast_channel *ooh323_new(struct ooh323_pvt *i, int state, >- const char *host, int capability, const char *linkedid) >+ const char *host, int capability, const char *linkedid) > { > struct ast_channel *ch = NULL; > int fmt = 0; >+ int features = 0; >+ > if (gH323Debug) > ast_verbose("--- ooh323_new - %s, %d\n", host, capability); > >@@ -390,18 +436,29 @@ > ast_module_ref(myself); > > /* Allocate dsp for in-band DTMF support */ >- if (i->dtmfmode & H323_DTMF_INBAND) { >+ if ((i->dtmfmode & H323_DTMF_INBAND) || (i->faxdetect & FAXDETECT_CNG)) { > i->vad = ast_dsp_new(); >- ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); >- ast_dsp_set_features(i->vad, >- DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_FAX_DETECT); >- ast_dsp_set_faxmode(i->vad, >- DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED); >+ } > >- if (i->dtmfmode & H323_DTMF_INBANDRELAX) >+ /* inband DTMF*/ >+ if (i->dtmfmode & H323_DTMF_INBAND) { >+ features |= DSP_FEATURE_DIGIT_DETECT; >+ if (i->dtmfmode & H323_DTMF_INBANDRELAX) { > ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); >+ } > } > >+ /* fax detection*/ >+ if (i->faxdetect & FAXDETECT_CNG) { >+ features |= DSP_FEATURE_FAX_DETECT; >+ ast_dsp_set_faxmode(i->vad, >+ DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED); >+ } >+ >+ if (features) { >+ ast_dsp_set_features(i->vad, features); >+ } >+ > ast_mutex_lock(&usecnt_lock); > usecnt++; > ast_mutex_unlock(&usecnt_lock); >@@ -517,6 +574,9 @@ > ast_udptl_set_error_correction_scheme(pvt->udptl, UDPTL_ERROR_CORRECTION_NONE); > ast_udptl_set_far_max_datagram(pvt->udptl, 144); > pvt->faxmode = 0; >+ pvt->chmodepend = 0; >+ pvt->faxdetected = 0; >+ pvt->faxdetect = gFAXdetect; > pvt->t38support = gT38Support; > pvt->rtptimeout = gRTPTimeout; > pvt->rtdrinterval = gRTDRInterval; >@@ -646,6 +706,7 @@ > p->g729onlyA = peer->g729onlyA; > p->dtmfmode |= peer->dtmfmode; > p->dtmfcodec = peer->dtmfcodec; >+ p->faxdetect = peer->faxdetect; > p->t38support = peer->t38support; > p->rtptimeout = peer->rtptimeout; > p->faststart = peer->faststart; >@@ -675,6 +736,7 @@ > p->g729onlyA = g729onlyA; > p->dtmfmode = gDTMFMode; > p->dtmfcodec = gDTMFCodec; >+ p->faxdetect = gFAXdetect; > p->t38support = gT38Support; > p->rtptimeout = gRTPTimeout; > p->capability = gCapability; >@@ -864,17 +926,7 @@ > } > ast_mutex_lock(&p->lock); > >- >- if (digit == 'e' && !p->faxmode && p->t38support != T38_DISABLED) { >- if (!p->chmodepend) { >- if (gH323Debug) >- ast_verbose("request to change %s to t.38 because fax cng\n", >- p->callToken); >- p->chmodepend = 1; >- ooRequestChangeMode(p->callToken, 1); >- } >- >- } else if (p->rtp && ((p->dtmfmode & H323_DTMF_RFC2833) || (p->dtmfmode & H323_DTMF_CISCO))) { >+ if (p->rtp && ((p->dtmfmode & H323_DTMF_RFC2833) || (p->dtmfmode & H323_DTMF_CISCO))) { > ast_rtp_instance_dtmf_begin(p->rtp, digit); > } else if (((p->dtmfmode & H323_DTMF_Q931) || > (p->dtmfmode & H323_DTMF_H245ALPHANUMERIC) || >@@ -1309,27 +1361,50 @@ > (int)sizeof(enum ast_control_t38), (int)datalen); > } else { > const struct ast_control_t38_parameters *parameters = data; >+ struct ast_control_t38_parameters our_parameters; > enum ast_control_t38 message = parameters->request_response; > switch (message) { > >+ case AST_T38_NEGOTIATED: >+ if (p->faxmode) { >+ res = 0; >+ break; >+ } > case AST_T38_REQUEST_NEGOTIATE: > >- if (!p->chmodepend && !p->faxmode) { >+ if (p->faxmode) { >+ /* T.38 already negotiated */ >+ our_parameters.request_response = AST_T38_NEGOTIATED; >+ our_parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); >+ our_parameters.rate = AST_T38_RATE_14400; >+ ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters)); >+ } else if (!p->chmodepend) { >+ p->chmodepend = 1; > ooRequestChangeMode(p->callToken, 1); >- p->chmodepend = 1; > res = 0; > } > break; > > case AST_T38_REQUEST_TERMINATE: > >- if (!p->chmodepend && p->faxmode) { >+ if (!p->faxmode) { >+ /* T.38 already terminated */ >+ our_parameters.request_response = AST_T38_TERMINATED; >+ ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters)); >+ } else if (!p->chmodepend) { >+ p->chmodepend = 1; > ooRequestChangeMode(p->callToken, 0); >- p->chmodepend = 1; > res = 0; > } > break; > >+ case AST_T38_REQUEST_PARMS: >+ our_parameters.request_response = AST_T38_REQUEST_PARMS; >+ our_parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); >+ our_parameters.rate = AST_T38_RATE_14400; >+ ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters)); >+ res = AST_T38_REQUEST_PARMS; >+ break; > > default: > ; >@@ -1375,17 +1450,18 @@ > case AST_OPTION_T38_STATE: > > if (*datalen != sizeof(enum ast_t38_state)) { >- ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option." >+ ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option." > " Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen); > break; > } >- if (p->t38support != T38_DISABLED) >- state = T38_STATE_UNKNOWN; >- if (p->faxmode) >- state = (p->chmodepend) ? T38_STATE_UNKNOWN : T38_STATE_NEGOTIATED; >- else if (p->chmodepend) >- state = T38_STATE_NEGOTIATING; > >+ if (p->t38support != T38_DISABLED) { >+ if (p->faxmode) { >+ state = (p->chmodepend) ? T38_STATE_NEGOTIATING : T38_STATE_NEGOTIATED; >+ } else { >+ state = T38_STATE_UNKNOWN; >+ } >+ } > > *((enum ast_t38_state *) data) = state; > res = 0; >@@ -1803,6 +1879,7 @@ > memcpy(&p->prefs, &user->prefs, sizeof(struct ast_codec_pref)); > p->dtmfmode |= user->dtmfmode; > p->dtmfcodec = user->dtmfcodec; >+ p->faxdetect = user->faxdetect; > p->t38support = user->t38support; > p->rtptimeout = user->rtptimeout; > p->h245tunneling = user->h245tunneling; >@@ -2225,6 +2302,7 @@ > user->rtptimeout = gRTPTimeout; > user->dtmfmode = gDTMFMode; > user->dtmfcodec = gDTMFCodec; >+ user->faxdetect = gFAXdetect; > user->t38support = gT38Support; > user->faststart = gFastStart; > user->h245tunneling = gTunneling; >@@ -2300,7 +2378,27 @@ > user->dtmfmode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0; > } else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) { > user->dtmfcodec = atoi(v->value); >- } else if (!strcasecmp(v->name, "t38support")) { >+ } else if (!strcasecmp(v->name, "faxdetect")) { >+ if (ast_true(v->value)) { >+ user->faxdetect = FAXDETECT_CNG | FAXDETECT_T38; >+ } else if (ast_false(v->value)) { >+ user->faxdetect = 0; >+ } else { >+ char *buf = ast_strdupa(v->value); >+ char *word, *next = buf; >+ user->faxdetect = 0; >+ while ((word = strsep(&next, ","))) { >+ if (!strcasecmp(word, "cng")) { >+ user->faxdetect |= FAXDETECT_CNG; >+ } else if (!strcasecmp(word, "t38")) { >+ user->faxdetect |= FAXDETECT_T38; >+ } else { >+ ast_log(LOG_WARNING, "Unknown faxdetect mode '%s' on line %d.\n", word, v->lineno); >+ } >+ } >+ >+ } >+ } else if (!strcasecmp(v->name, "t38support")) { > if (!strcasecmp(v->value, "disabled")) > user->t38support = T38_DISABLED; > if (!strcasecmp(v->value, "no")) >@@ -2339,11 +2437,12 @@ > peer->amaflags = gAMAFLAGS; > peer->dtmfmode = gDTMFMode; > peer->dtmfcodec = gDTMFCodec; >+ peer->faxdetect = gFAXdetect; > peer->t38support = gT38Support; > peer->faststart = gFastStart; > peer->h245tunneling = gTunneling; > peer->g729onlyA = g729onlyA; >- peer->port = 1720; >+ peer->port = 1720; > if (0 == friend_type) { > peer->mFriend = 1; > } >@@ -2439,7 +2538,27 @@ > peer->dtmfmode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0; > } else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) { > peer->dtmfcodec = atoi(v->value); >- } else if (!strcasecmp(v->name, "t38support")) { >+ } else if (!strcasecmp(v->name, "faxdetect")) { >+ if (ast_true(v->value)) { >+ peer->faxdetect = FAXDETECT_CNG | FAXDETECT_T38; >+ } else if (ast_false(v->value)) { >+ peer->faxdetect = 0; >+ } else { >+ char *buf = ast_strdupa(v->value); >+ char *word, *next = buf; >+ peer->faxdetect = 0; >+ while ((word = strsep(&next, ","))) { >+ if (!strcasecmp(word, "cng")) { >+ peer->faxdetect |= FAXDETECT_CNG; >+ } else if (!strcasecmp(word, "t38")) { >+ peer->faxdetect |= FAXDETECT_T38; >+ } else { >+ ast_log(LOG_WARNING, "Unknown faxdetect mode '%s' on line %d.\n", word, v->lineno); >+ } >+ } >+ >+ } >+ } else if (!strcasecmp(v->name, "t38support")) { > if (!strcasecmp(v->value, "disabled")) > peer->t38support = T38_DISABLED; > if (!strcasecmp(v->value, "no")) >@@ -2560,6 +2679,7 @@ > memset(&gPrefs, 0, sizeof(struct ast_codec_pref)); > gDTMFMode = H323_DTMF_RFC2833; > gDTMFCodec = 101; >+ gFAXdetect = FAXDETECT_CNG; > gT38Support = T38_FAXGW; > gTRCLVL = OOTRCLVLERR; > gRasGkMode = RasNoGatekeeper; >@@ -2756,7 +2876,27 @@ > gDTMFMode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0; > } else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) { > gDTMFCodec = atoi(v->value); >- } else if (!strcasecmp(v->name, "t38support")) { >+ } else if (!strcasecmp(v->name, "faxdetect")) { >+ if (ast_true(v->value)) { >+ gFAXdetect = FAXDETECT_CNG | FAXDETECT_T38; >+ } else if (ast_false(v->value)) { >+ gFAXdetect = 0; >+ } else { >+ char *buf = ast_strdupa(v->value); >+ char *word, *next = buf; >+ gFAXdetect = 0; >+ while ((word = strsep(&next, ","))) { >+ if (!strcasecmp(word, "cng")) { >+ gFAXdetect |= FAXDETECT_CNG; >+ } else if (!strcasecmp(word, "t38")) { >+ gFAXdetect |= FAXDETECT_T38; >+ } else { >+ ast_log(LOG_WARNING, "Unknown faxdetect mode '%s' on line %d.\n", word, v->lineno); >+ } >+ } >+ >+ } >+ } else if (!strcasecmp(v->name, "t38support")) { > if (!strcasecmp(v->value, "disabled")) > gT38Support = T38_DISABLED; > if (!strcasecmp(v->value, "no")) >@@ -2843,14 +2983,13 @@ > if (a->argc != 4) > return CLI_SHOWUSAGE; > >- > ast_mutex_lock(&peerl.lock); > peer = peerl.peers; > while (peer) { > ast_mutex_lock(&peer->lock); >- if(!strcmp(peer->name, a->argv[3])) >+ if (!strcmp(peer->name, a->argv[3])) { > break; >- else { >+ } else { > prev = peer; > peer = peer->next; > ast_mutex_unlock(&prev->lock); >@@ -2858,53 +2997,64 @@ > } > > if (peer) { >- sprintf(ip_port, "%s:%d", peer->ip, peer->port); >- ast_cli(a->fd, "%-15.15s%s\n", "Name: ", peer->name); >- ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", peer->faststart?"yes":"no", >+ sprintf(ip_port, "%s:%d", peer->ip, peer->port); >+ ast_cli(a->fd, "%-15.15s%s\n", "Name: ", peer->name); >+ ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", peer->faststart?"yes":"no", > peer->h245tunneling?"yes":"no"); >- ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); >- print_codec_to_cli(a->fd, &peer->prefs); >- ast_cli(a->fd, ")\n"); >- ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); >+ ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); >+ print_codec_to_cli(a->fd, &peer->prefs); >+ ast_cli(a->fd, ")\n"); >+ ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); > if (peer->dtmfmode & H323_DTMF_CISCO) { >- ast_cli(a->fd, "%s\n", "cisco"); >- ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", peer->dtmfcodec); >+ ast_cli(a->fd, "%s\n", "cisco"); >+ ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", peer->dtmfcodec); > } else if (peer->dtmfmode & H323_DTMF_RFC2833) { >- ast_cli(a->fd, "%s\n", "rfc2833"); >- ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", peer->dtmfcodec); >- } else if (peer->dtmfmode & H323_DTMF_Q931) >- ast_cli(a->fd, "%s\n", "q931keypad"); >- else if (peer->dtmfmode & H323_DTMF_H245ALPHANUMERIC) >- ast_cli(a->fd, "%s\n", "h245alphanumeric"); >- else if (peer->dtmfmode & H323_DTMF_H245SIGNAL) >- ast_cli(a->fd, "%s\n", "h245signal"); >- else if (peer->dtmfmode & H323_DTMF_INBAND && peer->dtmfmode & H323_DTMF_INBANDRELAX) >- ast_cli(a->fd, "%s\n", "inband-relaxed"); >- else if (peer->dtmfmode & H323_DTMF_INBAND) >- ast_cli(a->fd, "%s\n", "inband"); >- else >- ast_cli(a->fd, "%s\n", "unknown"); >+ ast_cli(a->fd, "%s\n", "rfc2833"); >+ ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", peer->dtmfcodec); >+ } else if (peer->dtmfmode & H323_DTMF_Q931) { >+ ast_cli(a->fd, "%s\n", "q931keypad"); >+ } else if (peer->dtmfmode & H323_DTMF_H245ALPHANUMERIC) { >+ ast_cli(a->fd, "%s\n", "h245alphanumeric"); >+ } else if (peer->dtmfmode & H323_DTMF_H245SIGNAL) { >+ ast_cli(a->fd, "%s\n", "h245signal"); >+ } else if (peer->dtmfmode & H323_DTMF_INBAND && peer->dtmfmode & H323_DTMF_INBANDRELAX) { >+ ast_cli(a->fd, "%s\n", "inband-relaxed"); >+ } else if (peer->dtmfmode & H323_DTMF_INBAND) { >+ ast_cli(a->fd, "%s\n", "inband"); >+ } else { >+ ast_cli(a->fd, "%s\n", "unknown"); >+ } >+ ast_cli(a->fd,"%-15s", "T.38 Mode: "); >+ if (peer->t38support == T38_DISABLED) { >+ ast_cli(a->fd, "%s\n", "disabled"); >+ } else if (peer->t38support == T38_FAXGW) { >+ ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); >+ } >+ if (peer->faxdetect == (FAXDETECT_CNG | FAXDETECT_T38)) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Yes"); >+ } else if (peer->faxdetect & FAXDETECT_CNG) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Cng"); >+ } else if (peer->faxdetect & FAXDETECT_T38) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "T.38"); >+ } else { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "No"); >+ } > >- ast_cli(a->fd,"%-15s", "T.38 Mode: "); >- if (peer->t38support == T38_DISABLED) >- ast_cli(a->fd, "%s\n", "disabled"); >- else if (peer->t38support == T38_FAXGW) >- ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); >- >- ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode); >- ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", >- ast_cdr_flags2str(peer->amaflags)); >- ast_cli(a->fd, "%-15.15s%s\n", "IP:Port: ", ip_port); >- ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit); >- ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout); >- if (peer->rtpmaskstr[0]) >- ast_cli(a->fd, "%-15.15s%s\n", "rtpmask: ", peer->rtpmaskstr); >- if (peer->rtdrcount && peer->rtdrinterval) >- ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", peer->rtdrcount, peer->rtdrinterval); >- ast_mutex_unlock(&peer->lock); >+ ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode); >+ ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(peer->amaflags)); >+ ast_cli(a->fd, "%-15.15s%s\n", "IP:Port: ", ip_port); >+ ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit); >+ ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout); >+ if (peer->rtpmaskstr[0]) { >+ ast_cli(a->fd, "%-15.15s%s\n", "rtpmask: ", peer->rtpmaskstr); >+ } >+ if (peer->rtdrcount && peer->rtdrinterval) { >+ ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", peer->rtdrcount, peer->rtdrinterval); >+ } >+ ast_mutex_unlock(&peer->lock); > } else { >- ast_cli(a->fd, "Peer %s not found\n", a->argv[3]); >- ast_cli(a->fd, "\n"); >+ ast_cli(a->fd, "Peer %s not found\n", a->argv[3]); >+ ast_cli(a->fd, "\n"); > } > ast_mutex_unlock(&peerl.lock); > >@@ -2994,9 +3144,9 @@ > user = userl.users; > while (user) { > ast_mutex_lock(&user->lock); >- if(!strcmp(user->name, a->argv[3])) { >+ if (!strcmp(user->name, a->argv[3])) { > break; >- } else { >+ } else { > prev = user; > user = user->next; > ast_mutex_unlock(&prev->lock); >@@ -3004,53 +3154,64 @@ > } > > if (user) { >- ast_cli(a->fd, "%-15.15s%s\n", "Name: ", user->name); >- ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", user->faststart?"yes":"no", >+ ast_cli(a->fd, "%-15.15s%s\n", "Name: ", user->name); >+ ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", user->faststart?"yes":"no", > user->h245tunneling?"yes":"no"); >- ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); >- print_codec_to_cli(a->fd, &user->prefs); >- ast_cli(a->fd, ")\n"); >- ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); >+ ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); >+ print_codec_to_cli(a->fd, &user->prefs); >+ ast_cli(a->fd, ")\n"); >+ ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); > if (user->dtmfmode & H323_DTMF_CISCO) { >- ast_cli(a->fd, "%s\n", "cisco"); >- ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", user->dtmfcodec); >+ ast_cli(a->fd, "%s\n", "cisco"); >+ ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", user->dtmfcodec); > } else if (user->dtmfmode & H323_DTMF_RFC2833) { >- ast_cli(a->fd, "%s\n", "rfc2833"); >- ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", user->dtmfcodec); >- } else if (user->dtmfmode & H323_DTMF_Q931) >- ast_cli(a->fd, "%s\n", "q931keypad"); >- else if (user->dtmfmode & H323_DTMF_H245ALPHANUMERIC) >- ast_cli(a->fd, "%s\n", "h245alphanumeric"); >- else if (user->dtmfmode & H323_DTMF_H245SIGNAL) >- ast_cli(a->fd, "%s\n", "h245signal"); >- else if (user->dtmfmode & H323_DTMF_INBAND && user->dtmfmode & H323_DTMF_INBANDRELAX) >- ast_cli(a->fd, "%s\n", "inband-relaxed"); >- else if (user->dtmfmode & H323_DTMF_INBAND) >- ast_cli(a->fd, "%s\n", "inband"); >- else >- ast_cli(a->fd, "%s\n", "unknown"); >+ ast_cli(a->fd, "%s\n", "rfc2833"); >+ ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", user->dtmfcodec); >+ } else if (user->dtmfmode & H323_DTMF_Q931) { >+ ast_cli(a->fd, "%s\n", "q931keypad"); >+ } else if (user->dtmfmode & H323_DTMF_H245ALPHANUMERIC) { >+ ast_cli(a->fd, "%s\n", "h245alphanumeric"); >+ } else if (user->dtmfmode & H323_DTMF_H245SIGNAL) { >+ ast_cli(a->fd, "%s\n", "h245signal"); >+ } else if (user->dtmfmode & H323_DTMF_INBAND && user->dtmfmode & H323_DTMF_INBANDRELAX) { >+ ast_cli(a->fd, "%s\n", "inband-relaxed"); >+ } else if (user->dtmfmode & H323_DTMF_INBAND) { >+ ast_cli(a->fd, "%s\n", "inband"); >+ } else { >+ ast_cli(a->fd, "%s\n", "unknown"); >+ } >+ ast_cli(a->fd,"%-15s", "T.38 Mode: "); >+ if (user->t38support == T38_DISABLED) { >+ ast_cli(a->fd, "%s\n", "disabled"); >+ } else if (user->t38support == T38_FAXGW) { >+ ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); >+ } >+ if (user->faxdetect == (FAXDETECT_CNG | FAXDETECT_T38)) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Yes"); >+ } else if (user->faxdetect & FAXDETECT_CNG) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Cng"); >+ } else if (user->faxdetect & FAXDETECT_T38) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "T.38"); >+ } else { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "No"); >+ } > >- ast_cli(a->fd,"%-15s", "T.38 Mode: "); >- if (user->t38support == T38_DISABLED) >- ast_cli(a->fd, "%s\n", "disabled"); >- else if (user->t38support == T38_FAXGW) >- ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); >- >- ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode); >- ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", >- ast_cdr_flags2str(user->amaflags)); >- ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context); >- ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit); >- ast_cli(a->fd, "%-15.15s%d\n", "InUse: ", user->inUse); >- ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", user->rtptimeout); >- if (user->rtpmaskstr[0]) >- ast_cli(a->fd, "%-15.15s%s\n", "rtpmask: ", user->rtpmaskstr); >+ ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode); >+ ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(user->amaflags)); >+ ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context); >+ ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit); >+ ast_cli(a->fd, "%-15.15s%d\n", "InUse: ", user->inUse); >+ ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", user->rtptimeout); >+ if (user->rtpmaskstr[0]) { >+ ast_cli(a->fd, "%-15.15s%s\n", "rtpmask: ", user->rtpmaskstr); >+ } > ast_mutex_unlock(&user->lock); >- if (user->rtdrcount && user->rtdrinterval) >- ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", user->rtdrcount, user->rtdrinterval); >+ if (user->rtdrcount && user->rtdrinterval) { >+ ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", user->rtdrcount, user->rtdrinterval); >+ } > } else { >- ast_cli(a->fd, "User %s not found\n", a->argv[3]); >- ast_cli(a->fd, "\n"); >+ ast_cli(a->fd, "User %s not found\n", a->argv[3]); >+ ast_cli(a->fd, "\n"); > } > ast_mutex_unlock(&userl.lock); > >@@ -3149,91 +3310,93 @@ > if (a->argc != 3) > return CLI_SHOWUSAGE; > >- >- >- ast_cli(a->fd, "\nObjective Open H.323 Channel Driver's Config:\n"); >+ ast_cli(a->fd, "\nObjective Open H.323 Channel Driver's Config:\n"); > snprintf(value, sizeof(value), "%s:%d", gIP, gPort); >- ast_cli(a->fd, "%-20s%s\n", "IP:Port: ", value); >- ast_cli(a->fd, "%-20s%d-%d\n", "H.225 port range: ", >- ooconfig.mTCPPortStart, ooconfig.mTCPPortEnd); >- ast_cli(a->fd, "%-20s%s\n", "FastStart", gFastStart?"yes":"no"); >- ast_cli(a->fd, "%-20s%s\n", "Tunneling", gTunneling?"yes":"no"); >- ast_cli(a->fd, "%-20s%s\n", "CallerId", gCallerID); >- ast_cli(a->fd, "%-20s%s\n", "MediaWaitForConnect", >- gMediaWaitForConnect?"yes":"no"); >+ ast_cli(a->fd, "%-20s%s\n", "IP:Port: ", value); >+ ast_cli(a->fd, "%-20s%d-%d\n", "H.225 port range: ", ooconfig.mTCPPortStart, ooconfig.mTCPPortEnd); >+ ast_cli(a->fd, "%-20s%s\n", "FastStart", gFastStart?"yes":"no"); >+ ast_cli(a->fd, "%-20s%s\n", "Tunneling", gTunneling?"yes":"no"); >+ ast_cli(a->fd, "%-20s%s\n", "CallerId", gCallerID); >+ ast_cli(a->fd, "%-20s%s\n", "MediaWaitForConnect", gMediaWaitForConnect?"yes":"no"); > > #if (0) > extern OOH323EndPoint gH323ep; >- ast_cli(a->fd, "%-20s%s\n", "FASTSTART", >- (OO_TESTFLAG(gH323ep.flags, OO_M_FASTSTART) != 0) ? "yes" : "no"); >- ast_cli(a->fd, "%-20s%s\n", "TUNNELING", >- (OO_TESTFLAG(gH323ep.flags, OO_M_TUNNELING) != 0) ? "yes" : "no"); >- ast_cli(a->fd, "%-20s%s\n", "MEDIAWAITFORCONN", >- (OO_TESTFLAG(gH323ep.flags, OO_M_MEDIAWAITFORCONN) != 0) ? "yes" : "no"); >+ ast_cli(a->fd, "%-20s%s\n", "FASTSTART", >+ (OO_TESTFLAG(gH323ep.flags, OO_M_FASTSTART) != 0) ? "yes" : "no"); >+ ast_cli(a->fd, "%-20s%s\n", "TUNNELING", >+ (OO_TESTFLAG(gH323ep.flags, OO_M_TUNNELING) != 0) ? "yes" : "no"); >+ ast_cli(a->fd, "%-20s%s\n", "MEDIAWAITFORCONN", >+ (OO_TESTFLAG(gH323ep.flags, OO_M_MEDIAWAITFORCONN) != 0) ? "yes" : "no"); > #endif > >- if (gRasGkMode == RasNoGatekeeper) >+ if (gRasGkMode == RasNoGatekeeper) { > snprintf(value, sizeof(value), "%s", "No Gatekeeper"); >- else if (gRasGkMode == RasDiscoverGatekeeper) >+ } else if (gRasGkMode == RasDiscoverGatekeeper) { > snprintf(value, sizeof(value), "%s", "Discover"); >- else >+ } else { > snprintf(value, sizeof(value), "%s", gGatekeeper); >- >- ast_cli(a->fd, "%-20s%s\n", "Gatekeeper:", value); >- >- ast_cli(a->fd, "%-20s%s\n", "H.323 LogFile:", gLogFile); >- >- ast_cli(a->fd, "%-20s%s\n", "Context:", gContext); >- >- ast_cli(a->fd, "%-20s%s\n", "Capability:", >- ast_getformatname_multiple(value,FORMAT_STRING_SIZE,gCapability)); >- >- ast_cli(a->fd, "%-20s", "DTMF Mode: "); >+ } >+ ast_cli(a->fd, "%-20s%s\n", "Gatekeeper:", value); >+ ast_cli(a->fd, "%-20s%s\n", "H.323 LogFile:", gLogFile); >+ ast_cli(a->fd, "%-20s%s\n", "Context:", gContext); >+ ast_cli(a->fd, "%-20s%s\n", "Capability:", >+ ast_getformatname_multiple(value,FORMAT_STRING_SIZE,gCapability)); >+ ast_cli(a->fd, "%-20s", "DTMF Mode: "); > if (gDTMFMode & H323_DTMF_CISCO) { >- ast_cli(a->fd, "%s\n", "cisco"); >- ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", gDTMFCodec); >+ ast_cli(a->fd, "%s\n", "cisco"); >+ ast_cli(a->fd, "%-20.15s%d\n", "DTMF Codec: ", gDTMFCodec); > } else if (gDTMFMode & H323_DTMF_RFC2833) { >- ast_cli(a->fd, "%s\n", "rfc2833"); >- ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", gDTMFCodec); >- } else if (gDTMFMode & H323_DTMF_Q931) >- ast_cli(a->fd, "%s\n", "q931keypad"); >- else if (gDTMFMode & H323_DTMF_H245ALPHANUMERIC) >- ast_cli(a->fd, "%s\n", "h245alphanumeric"); >- else if (gDTMFMode & H323_DTMF_H245SIGNAL) >- ast_cli(a->fd, "%s\n", "h245signal"); >- else if (gDTMFMode & H323_DTMF_INBAND && gDTMFMode & H323_DTMF_INBANDRELAX) >- ast_cli(a->fd, "%s\n", "inband-relaxed"); >- else if (gDTMFMode & H323_DTMF_INBAND) >- ast_cli(a->fd, "%s\n", "inband"); >- else >+ ast_cli(a->fd, "%s\n", "rfc2833"); >+ ast_cli(a->fd, "%-20.15s%d\n", "DTMF Codec: ", gDTMFCodec); >+ } else if (gDTMFMode & H323_DTMF_Q931) { >+ ast_cli(a->fd, "%s\n", "q931keypad"); >+ } else if (gDTMFMode & H323_DTMF_H245ALPHANUMERIC) { >+ ast_cli(a->fd, "%s\n", "h245alphanumeric"); >+ } else if (gDTMFMode & H323_DTMF_H245SIGNAL) { >+ ast_cli(a->fd, "%s\n", "h245signal"); >+ } else if (gDTMFMode & H323_DTMF_INBAND && gDTMFMode & H323_DTMF_INBANDRELAX) { >+ ast_cli(a->fd, "%s\n", "inband-relaxed"); >+ } else if (gDTMFMode & H323_DTMF_INBAND) { >+ ast_cli(a->fd, "%s\n", "inband"); >+ } else { > ast_cli(a->fd, "%s\n", "unknown"); >+ } > >- ast_cli(a->fd,"%-20s", "T.38 Mode: "); >- if (gT38Support == T38_DISABLED) >+ ast_cli(a->fd,"%-20s", "T.38 Mode: "); >+ if (gT38Support == T38_DISABLED) { > ast_cli(a->fd, "%s\n", "disabled"); >- else if (gT38Support == T38_FAXGW) >+ } else if (gT38Support == T38_FAXGW) { > ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible"); >+ } >+ if (gFAXdetect == (FAXDETECT_CNG | FAXDETECT_T38)) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Yes"); >+ } else if (gFAXdetect & FAXDETECT_CNG) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Cng"); >+ } else if (gFAXdetect & FAXDETECT_T38) { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "T.38"); >+ } else { >+ ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "No"); >+ } > >- if (gRTDRCount && gRTDRInterval) >- ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", gRTDRCount, gRTDRInterval); >+ if (gRTDRCount && gRTDRInterval) { >+ ast_cli(a->fd, "%-20.15s%d,%d\n", "RoundTrip: ", gRTDRCount, gRTDRInterval); >+ } > >- ast_cli(a->fd, "%-20s%ld\n", "Call counter: ", callnumber); >- ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode); >+ ast_cli(a->fd, "%-20s%ld\n", "Call counter: ", callnumber); >+ ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode); >+ ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS)); > >- ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS)); >- > pAlias = gAliasList; >- if(pAlias) { >- ast_cli(a->fd, "%-20s\n", "Aliases: "); >- } >+ if(pAlias) { >+ ast_cli(a->fd, "%-20s\n", "Aliases: "); >+ } > while (pAlias) { > pAliasNext = pAlias->next; > if (pAliasNext) { >- ast_cli(a->fd,"\t%-30s\t%-30s\n",pAlias->value, pAliasNext->value); >+ ast_cli(a->fd,"\t%-30s\t%-30s\n",pAlias->value, pAliasNext->value); > pAlias = pAliasNext->next; >- } >- else{ >- ast_cli(a->fd,"\t%-30s\n",pAlias->value); >+ } else { >+ ast_cli(a->fd,"\t%-30s\n",pAlias->value); > pAlias = pAlias->next; > } > } >@@ -3250,7 +3413,116 @@ > AST_CLI_DEFINE(handle_cli_ooh323_reload, "reload ooh323 config") > }; > >+/*! \brief OOH323 Dialplan function - reads ooh323 settings */ >+static int function_ooh323_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) >+{ >+ struct ooh323_pvt *p = chan->tech_pvt; > >+ ast_channel_lock(chan); >+ if (!p) { >+ ast_channel_unlock(chan); >+ return -1; >+ } >+ >+ if (strcmp(chan->tech->type, "OOH323")) { >+ ast_log(LOG_ERROR, "This function is only supported on OOH323 channels, Channel is %s\n", chan->tech->type); >+ ast_channel_unlock(chan); >+ return -1; >+ } >+ >+ ast_mutex_lock(&p->lock); >+ if (!strcasecmp(data, "faxdetect")) { >+ ast_copy_string(buf, p->faxdetect ? "1" : "0", len); >+ } else if (!strcasecmp(data, "t38support")) { >+ ast_copy_string(buf, p->t38support ? "1" : "0", len); >+ } else if (!strcasecmp(data, "caller_h323id")) { >+ ast_copy_string(buf, p->caller_h323id, len); >+ } else if (!strcasecmp(data, "caller_dialeddigits")) { >+ ast_copy_string(buf, p->caller_dialedDigits, len); >+ } else if (!strcasecmp(data, "caller_email")) { >+ ast_copy_string(buf, p->caller_email, len); >+ } else if (!strcasecmp(data, "h323id_url")) { >+ ast_copy_string(buf, p->caller_url, len); >+ } else if (!strcasecmp(data, "callee_h323id")) { >+ ast_copy_string(buf, p->callee_h323id, len); >+ } else if (!strcasecmp(data, "callee_dialeddigits")) { >+ ast_copy_string(buf, p->callee_dialedDigits, len); >+ } else if (!strcasecmp(data, "callee_email")) { >+ ast_copy_string(buf, p->callee_email, len); >+ } else if (!strcasecmp(data, "callee_url")) { >+ ast_copy_string(buf, p->callee_url, len); >+ } >+ ast_mutex_unlock(&p->lock); >+ >+ ast_channel_unlock(chan); >+ return 0; >+} >+ >+/*! \brief OOH323 Dialplan function - writes ooh323 settings */ >+static int function_ooh323_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) >+{ >+ struct ooh323_pvt *p = chan->tech_pvt; >+ int res = -1; >+ >+ ast_channel_lock(chan); >+ if (!p) { >+ ast_channel_unlock(chan); >+ return -1; >+ } >+ >+ if (strcmp(chan->tech->type, "OOH323")) { >+ ast_log(LOG_ERROR, "This function is only supported on OOH323 channels, Channel is %s\n", chan->tech->type); >+ ast_channel_unlock(chan); >+ return -1; >+ } >+ >+ ast_mutex_lock(&p->lock); >+ if (!strcasecmp(data, "faxdetect")) { >+ if (ast_true(value)) { >+ p->faxdetect = 1; >+ res = 0; >+ } else if (ast_false(value)) { >+ p->faxdetect = 0; >+ res = 0; >+ } else { >+ char *buf = ast_strdupa(value); >+ char *word, *next = buf; >+ p->faxdetect = 0; >+ res = 0; >+ while ((word = strsep(&next, ","))) { >+ if (!strcasecmp(word, "cng")) { >+ p->faxdetect |= FAXDETECT_CNG; >+ } else if (!strcasecmp(word, "t38")) { >+ p->faxdetect |= FAXDETECT_T38; >+ } else { >+ ast_log(LOG_WARNING, "Unknown faxdetect mode '%s'.\n", word); >+ res = -1; >+ } >+ } >+ >+ } >+ } else if (!strcasecmp(data, "t38support")) { >+ if (ast_true(value)) { >+ p->t38support = 1; >+ res = 0; >+ } else { >+ p->t38support = 0; >+ res = 0; >+ } >+ } >+ ast_mutex_unlock(&p->lock); >+ ast_channel_unlock(chan); >+ >+ return res; >+} >+ >+/*! \brief Structure to declare a dialplan function: OOH323 */ >+static struct ast_custom_function ooh323_function = { >+ .name = "OOH323", >+ .read = function_ooh323_read, >+ .write = function_ooh323_write, >+}; >+ > static int load_module(void) > { > int res; >@@ -3407,6 +3679,9 @@ > restart_monitor(); > } > >+ /* Register dialplan functions */ >+ ast_custom_function_register(&ooh323_function); >+ > return 0; > } > >@@ -3681,7 +3956,7 @@ > ast_mutex_unlock(&userl.lock); > return 0; > } >- >+ > static int unload_module(void) > { > struct ooh323_pvt *p; >@@ -3796,8 +4071,11 @@ > } > ooH323EpDestroy(); > >+ /* Unregister dial plan functions */ >+ ast_custom_function_unregister(&ooh323_function); >+ > if (gH323Debug) { >- ast_verbose("+++ ooh323 unload_module \n"); >+ ast_verbose("+++ ooh323 unload_module \n"); > } > > return 0; >@@ -4262,6 +4540,7 @@ > { > /* Retrieve audio/etc from channel. Assumes p->lock is already held. */ > struct ast_frame *f; >+ struct ast_frame *dfr = NULL; > static struct ast_frame null_frame = { AST_FRAME_NULL, }; > switch (ast->fdno) { > case 0: >@@ -4286,25 +4565,60 @@ > f = &null_frame; > } > >- if (p->owner) { >+ if (p->owner && !p->faxmode && (f->frametype == AST_FRAME_VOICE)) { > /* We already hold the channel lock */ >- if (f->frametype == AST_FRAME_VOICE && !p->faxmode) { >- if (f->subclass.codec != p->owner->nativeformats) { >- ast_debug(1, "Oooh, voice format changed to %s\n", ast_getformatname(f->subclass.codec)); >- p->owner->nativeformats = f->subclass.codec; >- ast_set_read_format(p->owner, p->owner->readformat); >- ast_set_write_format(p->owner, p->owner->writeformat); >- } >+ if (f->subclass.codec != p->owner->nativeformats) { >+ ast_debug(1, "Oooh, voice format changed to %s\n", ast_getformatname(f->subclass.codec)); >+ p->owner->nativeformats = f->subclass.codec; >+ ast_set_read_format(p->owner, p->owner->readformat); >+ ast_set_write_format(p->owner, p->owner->writeformat); >+ } >+ if (((p->dtmfmode & H323_DTMF_INBAND) || (p->faxdetect && FAXDETECT_CNG)) && p->vad && >+ (f->subclass.codec == AST_FORMAT_SLINEAR || f->subclass.codec == AST_FORMAT_ALAW || >+ f->subclass.codec == AST_FORMAT_ULAW)) { >+ dfr = ast_frdup(f); >+ dfr = ast_dsp_process(p->owner, p->vad, dfr); >+ } >+ } else { >+ return f; >+ } > >- if ((p->dtmfmode & H323_DTMF_INBAND) && p->vad && >- (f->subclass.codec == AST_FORMAT_SLINEAR || f->subclass.codec == AST_FORMAT_ALAW || >- f->subclass.codec == AST_FORMAT_ULAW)) { >- f = ast_dsp_process(p->owner, p->vad, f); >- if (f && (f->frametype == AST_FRAME_DTMF)) >- ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass.integer); >+ /* process INBAND DTMF*/ >+ if (dfr && (dfr->frametype == AST_FRAME_DTMF) && ((dfr->subclass.integer == 'f') || (dfr->subclass.integer == 'e'))) { >+ ast_debug(1, "* Detected FAX Tone %s\n", (dfr->subclass.integer == 'e') ? "CED" : "CNG"); >+ /* Switch to T.38 ON CED*/ >+ if (!p->faxmode && !p->chmodepend && (dfr->subclass.integer == 'e') && (p->t38support != T38_DISABLED)) { >+ if (gH323Debug) >+ ast_verbose("request to change %s to t.38 because fax ced\n", p->callToken); >+ p->chmodepend = 1; >+ p->faxdetected = 1; >+ ooRequestChangeMode(p->callToken, 1); >+ } else if ((dfr->subclass.integer == 'f') && !p->faxdetected) { >+ const char *target_context = S_OR(p->owner->macrocontext, p->owner->context); >+ if ((strcmp(p->owner->exten, "fax")) && >+ (ast_exists_extension(p->owner, target_context, "fax", 1, >+ S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL)))) { >+ ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", p->owner->name); >+ pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten); >+ if (ast_async_goto(p->owner, target_context, "fax", 1)) { >+ ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name,target_context); >+ } >+ p->faxdetected = 1; >+ if (dfr) { >+ ast_frfree(dfr); >+ } >+ return &ast_null_frame; > } > } >+ } else if (dfr && dfr->frametype == AST_FRAME_DTMF) { >+ ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass.integer); >+ ast_frfree(f); >+ return dfr; > } >+ >+ if (dfr) { >+ ast_frfree(dfr); >+ } > return f; > } > >@@ -4326,6 +4640,7 @@ > if (gH323Debug) > ast_debug(1, "mode for %s is already %d\n", call->callToken, > t38mode); >+ p->chmodepend = 0; > ast_mutex_unlock(&p->lock); > return; > } >@@ -4336,11 +4651,13 @@ > DEADLOCK_AVOIDANCE(&p->lock); > } > if (!p->owner) { >+ p->chmodepend = 0; > ast_mutex_unlock(&p->lock); > ast_log(LOG_ERROR, "Channel has no owner\n"); > return; > } > } else { >+ p->chmodepend = 0; > ast_mutex_unlock(&p->lock); > ast_log(LOG_ERROR, "Channel has no owner\n"); > return; >@@ -4350,10 +4667,26 @@ > > > if (p->t38support == T38_ENABLED) { >+ struct ast_control_t38_parameters parameters = { .request_response = 0 }; > >+ if ((p->faxdetect & FAXDETECT_T38) && !p->faxdetected) { >+ const char *target_context; >+ ast_debug(1, "* Detected T.38 Request\n"); >+ target_context = S_OR(p->owner->macrocontext, p->owner->context); >+ if ((strcmp(p->owner->exten, "fax")) && >+ (ast_exists_extension(p->owner, target_context, "fax", 1, >+ S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL)))) { >+ ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", p->owner->name); >+ pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten); >+ if (ast_async_goto(p->owner, target_context, "fax", 1)) { >+ ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name,target_context); >+ } >+ } >+ p->faxdetected = 1; >+ } >+ > /* AST_T38_CONTROL mode */ > >- struct ast_control_t38_parameters parameters = { .request_response = 0 }; > parameters.request_response = AST_T38_REQUEST_NEGOTIATE; > if (call->T38FarMaxDatagram) { > ast_udptl_set_far_max_datagram(p->udptl, call->T38FarMaxDatagram); >@@ -4369,6 +4702,7 @@ > ¶meters, sizeof(parameters)); > p->faxmode = 1; > >+ > } > } else { > if (p->t38support == T38_ENABLED) { >@@ -4380,6 +4714,7 @@ > ¶meters, sizeof(parameters)); > } > p->faxmode = 0; >+ p->faxdetected = 0; > p->t38_init = 0; > } > >Index: include/asterisk/res_fax.h >=================================================================== >--- include/asterisk/res_fax.h (.../branches/1.8) (revision 336160) >+++ include/asterisk/res_fax.h (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -42,6 +42,8 @@ > AST_FAX_TECH_T38 = (1 << 3), > /*! sending mulitple documents supported */ > AST_FAX_TECH_MULTI_DOC = (1 << 4), >+ /*! T.38 - T.30 Gateway */ >+ AST_FAX_TECH_GATEWAY = (1 << 5), > }; > > /*! \brief fax modem capabilities */ >@@ -168,6 +170,10 @@ > struct ast_fax_t38_parameters our_t38_parameters; > /*! the other endpoint's T.38 session parameters, if any */ > struct ast_fax_t38_parameters their_t38_parameters; >+ /*! the id of the t.38 gateway framehook for this channel */ >+ int gateway_id; >+ /*! the timeout for this gateway in seconds */ >+ int gateway_timeout; > }; > > struct ast_fax_tech; >@@ -204,6 +210,9 @@ > struct ast_smoother *smoother; > }; > >+/* if this overlaps with any AST_FRFLAG_* values, problems will occur */ >+#define AST_FAX_FRFLAG_GATEWAY (1 << 13) >+ > /*! \brief used to register a FAX technology module with res_fax */ > struct ast_fax_tech { > /*! the type of fax session supported with this ast_fax_tech structure */ >Index: include/asterisk/dsp.h >=================================================================== >--- include/asterisk/dsp.h (.../branches/1.8) (revision 336160) >+++ include/asterisk/dsp.h (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -43,9 +43,11 @@ > #define DSP_FEATURE_CALL_PROGRESS (DSP_PROGRESS_TALK | DSP_PROGRESS_RINGING | DSP_PROGRESS_BUSY | DSP_PROGRESS_CONGESTION) > #define DSP_FEATURE_WAITDIALTONE (1 << 20) /*!< Enable dial tone detection */ > >-#define DSP_FAXMODE_DETECT_CNG (1 << 0) >-#define DSP_FAXMODE_DETECT_CED (1 << 1) >-#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED) >+#define DSP_FAXMODE_DETECT_CNG (1 << 0) >+#define DSP_FAXMODE_DETECT_CED (1 << 1) >+#define DSP_FAXMODE_DETECT_SQUELCH (1 << 2) >+#define DSP_FAXMODE_DETECT_V21 (1 << 3) >+#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21) > > #define DSP_TONE_STATE_SILENCE 0 > #define DSP_TONE_STATE_RINGING 1 >Index: main/dsp.c >=================================================================== >--- main/dsp.c (.../branches/1.8) (revision 336160) >+++ main/dsp.c (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -254,6 +254,20 @@ > > typedef struct > { >+ int block_size; >+ goertzel_state_t tone; >+ float energy; /* Accumulated energy of the current block */ >+ int samples_pending; /* Samples remain to complete the current block */ >+ >+ float threshold; /* Energy of the tone relative to energy from all other signals to consider a hit */ >+ >+ int hit_count; >+ int miss_count; >+ >+} v21_detect_state_t; >+ >+typedef struct >+{ > goertzel_state_t row_out[4]; > goertzel_state_t col_out[4]; > int hits_to_begin; /* How many successive hits needed to consider begin of a digit */ >@@ -399,6 +413,7 @@ > digit_detect_state_t digit_state; > tone_detect_state_t cng_tone_state; > tone_detect_state_t ced_tone_state; >+ v21_detect_state_t v21_state; > }; > > static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment) >@@ -471,10 +486,59 @@ > ast_debug(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required); > } > >+static void ast_v21_detect_init(v21_detect_state_t *s) >+{ >+ float x; >+ int periods_in_block; >+ >+ /* If we want to remove tone, it is important to have block size not >+ to exceed frame size. Otherwise by the moment tone is detected it is too late >+ to squelch it from previous frames. Block size is 20ms at the given sample rate.*/ >+ s->block_size = SAMPLES_IN_FRAME; >+ >+ periods_in_block = s->block_size * 1850 / SAMPLE_RATE; >+ >+ /* Make sure we will have at least 5 periods at target frequency for analisys. >+ This may make block larger than expected packet and will make squelching impossible >+ but at least we will be detecting the tone */ >+ if (periods_in_block < 5) >+ periods_in_block = 5; >+ >+ /* Now calculate final block size. It will contain integer number of periods */ >+ s->block_size = periods_in_block * SAMPLE_RATE / 1850; >+ >+ goertzel_init(&s->tone, 1850.0, s->block_size); >+ >+ s->samples_pending = s->block_size; >+ s->hit_count = 0; >+ s->miss_count = 0; >+ s->energy = 0.0; >+ >+ /* We want tone energy to be amp decibels above the rest of the signal (the noise). >+ According to Parseval's theorem the energy computed in time domain equals to energy >+ computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result) >+ from the energy in the time domain we will get energy of the remaining signal (without the tone >+ we are detecting). We will be checking that >+ 10*log(Ew / (Et - Ew)) > amp >+ Calculate threshold so that we will be actually checking >+ Ew > Et * threshold >+ */ >+ >+ x = pow(10.0, 16 / 10.0); >+ s->threshold = x / (x + 1); >+ >+ ast_debug(1, "Setup v21 detector, block_size=%d\n", s->block_size); >+} >+ > static void ast_fax_detect_init(struct ast_dsp *s) > { > ast_tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB); > ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB); >+ ast_v21_detect_init(&s->v21_state); >+ if (s->faxmode & DSP_FAXMODE_DETECT_SQUELCH) { >+ s->cng_tone_state.squelch = 1; >+ s->ced_tone_state.squelch = 1; >+ } > } > > static void ast_dtmf_detect_init (dtmf_detect_state_t *s) >@@ -521,6 +585,89 @@ > } > } > >+/*! \brief Detect a v21 preamble. >+ * This code is derived from the tone_detect code and detects a pattern of 1850 >+ * Hz tone found in a v21 preamble. >+ */ >+static int v21_detect(struct ast_dsp *dsp, v21_detect_state_t *s, int16_t *amp, int samples) >+{ >+ float tone_energy; >+ int i; >+ int hit = 0; >+ int limit; >+ int res = 0; >+ int16_t *ptr; >+ int start, end; >+ >+ for (start = 0; start < samples; start = end) { >+ /* Process in blocks. */ >+ limit = samples - start; >+ if (limit > s->samples_pending) { >+ limit = s->samples_pending; >+ } >+ end = start + limit; >+ >+ for (i = limit, ptr = amp ; i > 0; i--, ptr++) { >+ /* signed 32 bit int should be enough to suqare any possible signed 16 bit value */ >+ s->energy += (int32_t) *ptr * (int32_t) *ptr; >+ >+ goertzel_sample(&s->tone, *ptr); >+ } >+ >+ s->samples_pending -= limit; >+ >+ if (s->samples_pending) { >+ /* Finished incomplete (last) block */ >+ break; >+ } >+ >+ tone_energy = goertzel_result(&s->tone); >+ >+ /* Scale to make comparable */ >+ tone_energy *= 2.0; >+ s->energy *= s->block_size; >+ >+ ast_debug(10, "v21 1850 Ew=%.2E, Et=%.2E, s/n=%10.2f\n", tone_energy, s->energy, tone_energy / (s->energy - tone_energy)); >+ >+ hit = 0; >+ if (tone_energy > s->energy * s->threshold) { >+ ast_debug(10, "Hit! count=%d; miss_count=%d\n", s->hit_count, s->miss_count); >+ hit = 1; >+ } >+ >+ if (hit) { >+ if (s->hit_count == 0 || s->miss_count == 3) { >+ s->hit_count++; >+ } else { >+ s->hit_count = 0; >+ } >+ >+ s->miss_count = 0; >+ } else { >+ s->miss_count++; >+ if (s->miss_count > 3) { >+ s->hit_count = 0; >+ } >+ } >+ >+ if (s->hit_count == 4) { >+ ast_debug(1, "v21 preamble detected\n"); >+ res = 1; >+ } >+ >+ /* Reinitialise the detector for the next block */ >+ goertzel_reset(&s->tone); >+ >+ /* Advance to the next block */ >+ s->energy = 0.0; >+ s->samples_pending = s->block_size; >+ >+ amp += limit; >+ } >+ >+ return res; >+} >+ > static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp, int samples) > { > float tone_energy; >@@ -1385,6 +1532,10 @@ > if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len)) { > fax_digit = 'e'; > } >+ >+ if ((dsp->faxmode & DSP_FAXMODE_DETECT_V21) && v21_detect(dsp, &dsp->v21_state, shortdata, len)) { >+ fax_digit = 'g'; >+ } > } > > if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) { >@@ -1640,9 +1791,9 @@ > int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode) > { > if (dsp->faxmode != faxmode) { >+ dsp->faxmode = faxmode; > ast_fax_detect_init(dsp); > } >- dsp->faxmode = faxmode; > return 0; > } > >Index: main/frame.c >=================================================================== >--- main/frame.c (.../branches/1.8) (revision 336160) >+++ main/frame.c (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -413,7 +413,7 @@ > out->samples = fr->samples; > out->offset = fr->offset; > /* Copy the timing data */ >- ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO); >+ ast_copy_flags(out, fr, AST_FLAGS_ALL); > if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) { > out->ts = fr->ts; > out->len = fr->len; >@@ -537,7 +537,7 @@ > /* Must have space since we allocated for it */ > strcpy(src, f->src); > } >- ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO); >+ ast_copy_flags(out, f, AST_FLAGS_ALL); > out->ts = f->ts; > out->len = f->len; > out->seqno = f->seqno; >Index: configs/chan_ooh323.conf.sample >=================================================================== >--- configs/chan_ooh323.conf.sample (.../branches/1.8) (revision 336160) >+++ configs/chan_ooh323.conf.sample (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -122,6 +122,18 @@ > ; > ;roundtrip=x,y > >+; >+; FAX detection will cause the OOH323 channel to jump to the 'fax' extension (if it exists) >+; based one or more events being detected. The events that can be detected are an incoming >+; CNG tone or an incoming T.38 RequestMode packet >+; >+; yes - enable both detection (CNG & T.38) >+; no - disable both >+; cng - enable CNG detection (default) >+; t38 - enable T.38 request detection >+; >+faxdetect = cng >+ > ; User/peer/friend definitions: > ; User config options Peer config options > ; ------------------ ------------------- >Index: res/res_fax.c >=================================================================== >--- res/res_fax.c (.../branches/1.8) (revision 336160) >+++ res/res_fax.c (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -5,7 +5,24 @@ > * > * Dwayne M. Hubbard <dhubbard@digium.com> > * Kevin P. Fleming <kpfleming@digium.com> >+ * Matthew Nicholson <mnicholson@digium.com> > * >+ * Initial T.38-gateway code >+ * 2008, Daniel Ferenci <daniel.ferenci@nethemba.com> >+ * Created by Nethemba s.r.o. http://www.nethemba.com >+ * Sponsored by IPEX a.s. http://www.ipex.cz >+ * >+ * T.38-gateway integration into asterisk app_fax and rework >+ * 2008-2011, Gregory Hinton Nietsky <gregory@distrotech.co.za> >+ * dns Telecom http://www.dnstelecom.co.za >+ * >+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2 >+ * 2010, Anton Verevkin <mymail@verevkin.it> >+ * ViaNetTV http://www.vianettv.com >+ * >+ * Modified to make T.38-gateway work >+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at >+ * > * See http://www.asterisk.org for more information about > * the Asterisk project. Please do not directly contact > * any of the maintainers of this project for assistance; >@@ -27,6 +44,8 @@ > * > * \author Dwayne M. Hubbard <dhubbard@digium.com> > * \author Kevin P. Fleming <kpfleming@digium.com> >+ * \author Matthew Nicholson <mnicholson@digium.com> >+ * \author Gregory H. Nietsky <gregory@distrotech.co.za> > * > * A generic FAX resource module that provides SendFAX and ReceiveFAX applications. > * This module requires FAX technology modules, like res_fax_spandsp, to register with it >@@ -62,6 +81,7 @@ > #include "asterisk/dsp.h" > #include "asterisk/indications.h" > #include "asterisk/ast_version.h" >+#include "asterisk/translate.h" > > /*** DOCUMENTATION > <application name="ReceiveFax" language="en_US"> >@@ -163,6 +183,9 @@ > <enum name="modem"> > <para>R/W Modem type (v17/v27/v29).</para> > </enum> >+ <enum name="gateway"> >+ <para>R/W T38 Gateway Enabled With optional fax activity timeout in seconds (yes[,gwtimeout]/no)</para> >+ </enum> > <enum name="pages"> > <para>R/O Number of pages transferred.</para> > </enum> >@@ -196,10 +219,33 @@ > <ref type="application">SendFax</ref> > </see-also> > </function> >+ <application name="WaitFAX" language="en_US"> >+ <synopsis> >+ Generic Fax Detect CNG/T.38 (Wait For Fax) >+ </synopsis> >+ <syntax> >+ <parameter name="timeout" required="true"> >+ <para>Specifies the number of seconds we attempt to detect a fax tone on the channel</para> >+ </parameter> >+ <parameter name="tone" required="false"> >+ <para>Either the tone name defined in the "indications.conf" configuration file, >+ or a directly specified list of frequencies and durations.</para> >+ <para>If not specified silence is generated.</para> >+ </parameter> >+ <parameter name="noise" required="false"> >+ <para>Number of ms noise detected before proceeding with the dialplan.</para> >+ </parameter> >+ </syntax> >+ <description> >+ <para>This application sets FAXOPT(status) To SUCCESS | FAILURE | ERROR</para> >+ <para>FAXOPT(statusstr) will be set to CNG | T38 on SUCCESS or reason on FAILURE | ERROR</para> >+ </description> >+ </application> > ***/ > > static const char app_receivefax[] = "ReceiveFAX"; > static const char app_sendfax[] = "SendFAX"; >+static const char app_waitfax[] = "WaitFAX"; > > struct debug_info_history { > unsigned int consec_frames; >@@ -213,12 +259,39 @@ > struct ast_dsp *dsp; > }; > >+/*! \brief used for gateway framehook */ >+struct fax_gateway { >+ /*! \brief FAX Session */ >+ struct ast_fax_session *s; >+ /*! \brief reserved fax session token */ >+ struct ast_fax_tech_token *token; >+ /*! \brief the start of our timeout counter */ >+ struct timeval timeout_start; >+ /*! \brief DSP Processor */ >+ struct ast_dsp *chan_dsp; >+ struct ast_dsp *peer_dsp; >+ /*! \brief framehook used in gateway mode */ >+ int framehook; >+ /*! \brief bridged */ >+ int bridged:1; >+ /*! \brief 1 if a v21 preamble has been detected */ >+ int detected_v21:1; >+ /*! \brief a flag to track the state of our negotiation */ >+ enum ast_t38_state t38_state; >+ /*! \brief Original audio formats */ >+ unsigned int chan_read_format; >+ unsigned int chan_write_format; >+ unsigned int peer_read_format; >+ unsigned int peer_write_format; >+}; >+ > static int fax_logger_level = -1; > > /*! \brief maximum buckets for res_fax ao2 containers */ > #define FAX_MAXBUCKETS 10 > > #define RES_FAX_TIMEOUT 10000 >+#define FAX_GATEWAY_TIMEOUT RES_FAX_TIMEOUT > > /*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */ > static struct { >@@ -393,10 +466,40 @@ > d->modems = general_options.modems; > d->minrate = general_options.minrate; > d->maxrate = general_options.maxrate; >+ d->gateway_id = -1; > > return d; > } > >+static struct ast_control_t38_parameters our_t38_parameters = { >+ .version = 0, >+ .max_ifp = 400, >+ .rate = AST_T38_RATE_14400, >+ .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, >+}; >+ >+static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src) >+{ >+ dst->version = src->version; >+ dst->max_ifp = src->max_ifp; >+ dst->rate = src->rate; >+ dst->rate_management = src->rate_management; >+ dst->fill_bit_removal = src->fill_bit_removal; >+ dst->transcoding_mmr = src->transcoding_mmr; >+ dst->transcoding_jbig = src->transcoding_jbig; >+} >+ >+static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src) >+{ >+ dst->version = src->version; >+ dst->max_ifp = src->max_ifp; >+ dst->rate = src->rate; >+ dst->rate_management = src->rate_management; >+ dst->fill_bit_removal = src->fill_bit_removal; >+ dst->transcoding_mmr = src->transcoding_mmr; >+ dst->transcoding_jbig = src->transcoding_jbig; >+} >+ > /*! \brief returns a reference counted details structure from the channel's fax datastore. If the datastore > * does not exist it will be created */ > static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan) >@@ -419,6 +522,11 @@ > } > /* add the datastore to the channel and increment the refcount */ > datastore->data = details; >+ >+ /* initialize default T.38 parameters */ >+ t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters); >+ t38_parameters_ast_to_fax(&details->their_t38_parameters, &our_t38_parameters); >+ > ao2_ref(details, 1); > ast_channel_lock(chan); > ast_channel_datastore_add(chan, datastore); >@@ -511,6 +619,13 @@ > ast_build_string(&buf, &size, "MULTI_DOC"); > first = 0; > } >+ if (caps & AST_FAX_TECH_GATEWAY) { >+ if (!first) { >+ ast_build_string(&buf, &size, ","); >+ } >+ ast_build_string(&buf, &size, "T38_GATEWAY"); >+ first = 0; >+ } > > return out; > } >@@ -686,6 +801,19 @@ > } > } > >+/*! \brief Release a session token. >+ * \param s a session returned from fax_session_reserve() >+ * \param token a token generated from fax_session_reserve() >+ * >+ * This function releases the given token and marks the given session as no >+ * longer reserved. It is safe to call on a session that is not actually >+ * reserved and with a NULL token. This is so that sessions returned by >+ * technologies that do not support reserved sessions don't require extra logic >+ * to handle. >+ * >+ * \note This function DOES NOT release the given fax session, only the given >+ * token. >+ */ > static void fax_session_release(struct ast_fax_session *s, struct ast_fax_tech_token *token) > { > if (token) { >@@ -732,6 +860,19 @@ > ast_free(s->chan_uniqueid); > } > >+/*! \brief Reserve a fax session. >+ * \param details the fax session details >+ * \param token a pointer to a place to store a token to be passed to fax_session_new() later >+ * >+ * This function reserves a fax session for use later. If the selected fax >+ * technology does not support reserving sessions a session will still be >+ * returned but token will not be set. >+ * >+ * \note The reference returned by this function does not get consumed by >+ * fax_session_new() and must always be dereferenced separately. >+ * >+ * \return NULL or an uninitialized and possibly reserved session >+ */ > static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_details *details, struct ast_fax_tech_token **token) > { > struct ast_fax_session *s; >@@ -743,6 +884,8 @@ > } > > s->state = AST_FAX_STATE_INACTIVE; >+ s->details = details; >+ ao2_ref(s->details, 1); > > /* locate a FAX technology module that can handle said requirements > * Note: the requirements have not yet been finalized as T.38 >@@ -781,7 +924,22 @@ > return s; > } > >-/*! \brief create a FAX session */ >+/*! \brief create a FAX session >+ * >+ * \param details details for the session >+ * \param chan the channel the session will run on >+ * \param reserved a reserved session to base this session on (can be NULL) >+ * \param token the token for a reserved session (can be NULL) >+ * >+ * Create a new fax session based on the given details structure. >+ * >+ * \note The given token is always consumed (by tech->new_session() or by >+ * fax_session_release() in the event of a failure). The given reference to a >+ * reserved session is never consumed and must be dereferenced separately from >+ * the reference returned by this function. >+ * >+ * \return NULL or a reference to a new fax session >+ */ > static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *details, struct ast_channel *chan, struct ast_fax_session *reserved, struct ast_fax_tech_token *token) > { > struct ast_fax_session *s = NULL; >@@ -792,6 +950,12 @@ > s = reserved; > ao2_ref(reserved, +1); > >+ /* NOTE: we don't consume the reference to the reserved >+ * session. The session returned from fax_session_new() is a >+ * new reference and must be derefed in addition to the >+ * reserved session. >+ */ >+ > if (s->state == AST_FAX_STATE_RESERVED) { > ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1); > s->state = AST_FAX_STATE_UNINITIALIZED; >@@ -834,8 +998,10 @@ > } > > s->chan = chan; >- s->details = details; >- ao2_ref(s->details, 1); >+ if (!s->details) { >+ s->details = details; >+ ao2_ref(s->details, 1); >+ } > > details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1); > >@@ -935,9 +1101,6 @@ > static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status) > { > char *filenames = generate_filenames_string(details, "FileName: ", "\r\n"); >- if (!filenames) { >- return 1; >- } > > ast_channel_lock(chan); > if (details->option.statusevents) { >@@ -945,25 +1108,31 @@ > > get_manager_event_info(chan, &info); > manager_event(EVENT_FLAG_CALL, >- (details->caps & AST_FAX_TECH_RECEIVE) ? "ReceiveFAXStatus" : "SendFAXStatus", >+ "FAXStatus", >+ "Operation: %s\r\n" > "Status: %s\r\n" > "Channel: %s\r\n" > "Context: %s\r\n" > "Exten: %s\r\n" > "CallerID: %s\r\n" > "LocalStationID: %s\r\n" >- "%s\r\n", >+ "%s%s", >+ (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send", > status, > chan->name, > info.context, > info.exten, > info.cid, > details->localstationid, >- filenames); >+ S_OR(filenames, ""), >+ filenames ? "\r\n" : ""); > } > ast_channel_unlock(chan); >- ast_free(filenames); > >+ if (filenames) { >+ ast_free(filenames); >+ } >+ > return 0; > } > >@@ -1006,37 +1175,18 @@ > GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \ > } while (0) > >-static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src) >-{ >- dst->version = src->version; >- dst->max_ifp = src->max_ifp; >- dst->rate = src->rate; >- dst->rate_management = src->rate_management; >- dst->fill_bit_removal = src->fill_bit_removal; >- dst->transcoding_mmr = src->transcoding_mmr; >- dst->transcoding_jbig = src->transcoding_jbig; >-} >- >-static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src) >-{ >- dst->version = src->version; >- dst->max_ifp = src->max_ifp; >- dst->rate = src->rate; >- dst->rate_management = src->rate_management; >- dst->fill_bit_removal = src->fill_bit_removal; >- dst->transcoding_mmr = src->transcoding_mmr; >- dst->transcoding_jbig = src->transcoding_jbig; >-} >- > static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_details *details) > { > switch (ast_channel_get_t38_state(chan)) { > case T38_STATE_UNKNOWN: > details->caps |= AST_FAX_TECH_T38; > break; >+ case T38_STATE_REJECTED: > case T38_STATE_UNAVAILABLE: > details->caps |= AST_FAX_TECH_AUDIO; > break; >+ case T38_STATE_NEGOTIATED: >+ /* already in T.38 mode? This should not happen. */ > case T38_STATE_NEGOTIATING: { > /* the other end already sent us a T.38 reinvite, so we need to prod the channel > * driver into resending their parameters to us if it supports doing so... if >@@ -1118,13 +1268,6 @@ > return 0; > } > >-static struct ast_control_t38_parameters our_t38_parameters = { >- .version = 0, >- .max_ifp = 400, >- .rate = AST_T38_RATE_14400, >- .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, >-}; >- > /*! \brief this is the generic FAX session handling function */ > static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details, struct ast_fax_session *reserved, struct ast_fax_tech_token *token) > { >@@ -1390,8 +1533,6 @@ > struct ast_frame *frame = NULL; > struct ast_control_t38_parameters t38_parameters; > >- t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters); >- > /* don't send any audio if we've already received a T.38 reinvite */ > if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) { > /* generate 3 seconds of CED */ >@@ -1551,6 +1692,7 @@ > ); > struct ast_flags opts = { 0, }; > struct manager_event_info info; >+ enum ast_t38_state t38state; > > /* initialize output channel variables */ > pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED"); >@@ -1579,6 +1721,14 @@ > ast_string_field_set(details, error, "INIT_ERROR"); > set_channel_variables(chan, details); > >+ if (details->caps & AST_FAX_TECH_GATEWAY) { >+ ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway"); >+ set_channel_variables(chan, details); >+ ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n"); >+ ao2_ref(details, -1); >+ return -1; >+ } >+ > if (details->maxrate < details->minrate) { > ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); > ast_string_field_set(details, error, "INVALID_ARGUMENTS"); >@@ -1683,7 +1833,8 @@ > details->option.statusevents = AST_FAX_OPTFLAG_TRUE; > } > >- if ((ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE) || >+ t38state = ast_channel_get_t38_state(chan); >+ if ((t38state == T38_STATE_UNAVAILABLE) || (t38state == T38_STATE_REJECTED) || > ast_test_flag(&opts, OPT_ALLOWAUDIO)) { > details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; > } >@@ -1789,8 +1940,6 @@ > struct ast_frame *frame = NULL; > struct ast_control_t38_parameters t38_parameters; > >- t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters); >- > /* send CNG tone while listening for the receiver to initiate a switch > * to T.38 mode; if they do, stop sending the CNG tone and proceed with > * the switch. >@@ -2023,6 +2172,7 @@ > ); > struct ast_flags opts = { 0, }; > struct manager_event_info info; >+ enum ast_t38_state t38state; > > /* initialize output channel variables */ > pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED"); >@@ -2051,6 +2201,14 @@ > ast_string_field_set(details, error, "INIT_ERROR"); > set_channel_variables(chan, details); > >+ if (details->caps & AST_FAX_TECH_GATEWAY) { >+ ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway"); >+ set_channel_variables(chan, details); >+ ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n"); >+ ao2_ref(details, -1); >+ return -1; >+ } >+ > if (details->maxrate < details->minrate) { > ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); > ast_string_field_set(details, error, "INVALID_ARGUMENTS"); >@@ -2175,7 +2333,8 @@ > details->option.statusevents = AST_FAX_OPTFLAG_TRUE; > } > >- if ((ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE) || >+ t38state = ast_channel_get_t38_state(chan); >+ if ((t38state == T38_STATE_UNAVAILABLE) || (t38state == T38_STATE_REJECTED) || > ast_test_flag(&opts, OPT_ALLOWAUDIO)) { > details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; > } >@@ -2287,6 +2446,908 @@ > return (!channel_alive) ? -1 : 0; > } > >+/*! \brief destroy a FAX gateway session structure */ >+static void destroy_gateway(void *data) >+{ >+ struct fax_gateway *gateway = data; >+ >+ if (gateway->chan_dsp) { >+ ast_dsp_free(gateway->chan_dsp); >+ gateway->chan_dsp = NULL; >+ } >+ >+ if (gateway->peer_dsp) { >+ ast_dsp_free(gateway->peer_dsp); >+ gateway->peer_dsp = NULL; >+ } >+ >+ if (gateway->s) { >+ fax_session_release(gateway->s, gateway->token); >+ gateway->token = NULL; >+ gateway->s->details->caps |= ~AST_FAX_TECH_GATEWAY; >+ >+ ao2_lock(faxregistry.container); >+ ao2_unlink(faxregistry.container, gateway->s); >+ ao2_unlock(faxregistry.container); >+ >+ ao2_ref(gateway->s, -1); >+ gateway->s = NULL; >+ } >+} >+ >+/*! \brief Create a new fax gateway object. >+ * \param details the fax session details >+ * \return NULL or a fax gateway object >+ */ >+static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *details) >+{ >+ struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway); >+ if (!gateway) { >+ return NULL; >+ } >+ >+ gateway->chan_dsp = ast_dsp_new(); >+ if (!gateway->chan_dsp) { >+ ao2_ref(gateway, -1); >+ return NULL; >+ } >+ >+ gateway->peer_dsp = ast_dsp_new(); >+ if (!gateway->peer_dsp) { >+ ao2_ref(gateway, -1); >+ return NULL; >+ } >+ >+ gateway->framehook = -1; >+ >+ ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT); >+ ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21 | DSP_FAXMODE_DETECT_CED); >+ >+ ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT); >+ ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21 | DSP_FAXMODE_DETECT_CED); >+ >+ details->caps = AST_FAX_TECH_GATEWAY; >+ if (details->gateway_timeout && !(gateway->s = fax_session_reserve(details, &gateway->token))) { >+ details->caps |= ~AST_FAX_TECH_GATEWAY; >+ ast_log(LOG_ERROR, "Can't reserve a FAX session, gateway attempt failed.\n"); >+ ao2_ref(gateway, -1); >+ return NULL; >+ } >+ >+ return gateway; >+} >+ >+/*! \brief Create a fax session and start T.30<->T.38 gateway mode >+ * \param gateway a fax gateway object >+ * \param details fax session details >+ * \param chan active channel >+ * \return 0 on error 1 on success*/ >+static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan) >+{ >+ struct ast_fax_session *s; >+ >+ /* create the FAX session */ >+ if (!(s = fax_session_new(details, chan, gateway->s, gateway->token))) { >+ gateway->token = NULL; >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "error starting gateway session"); >+ ast_string_field_set(details, error, "INIT_ERROR"); >+ set_channel_variables(chan, details); >+ report_fax_status(chan, details, "No Available Resource"); >+ ast_log(LOG_ERROR, "Can't create a FAX session, gateway attempt failed.\n"); >+ return -1; >+ } >+ /* release the reference for the reserved session and replace it with >+ * the real session */ >+ ao2_ref(gateway->s, -1); >+ gateway->s = s; >+ gateway->token = NULL; >+ >+ if (gateway->s->tech->start_session(gateway->s) < 0) { >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "error starting gateway session"); >+ ast_string_field_set(details, error, "INIT_ERROR"); >+ set_channel_variables(chan, details); >+ return -1; >+ } >+ >+ gateway->timeout_start.tv_sec = 0; >+ gateway->timeout_start.tv_usec = 0; >+ >+ report_fax_status(chan, details, "FAX Transmission In Progress"); >+ >+ return 0; >+} >+ >+static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_frame *f) >+{ >+ struct ast_frame *fp; >+ struct ast_control_t38_parameters t38_parameters = { >+ .request_response = AST_T38_REQUEST_NEGOTIATE, >+ }; >+ struct ast_frame control_frame = { >+ .src = "res_fax", >+ .frametype = AST_FRAME_CONTROL, >+ .datalen = sizeof(t38_parameters), >+ .subclass.integer = AST_CONTROL_T38_PARAMETERS, >+ .data.ptr = &t38_parameters, >+ }; >+ >+ struct ast_fax_session_details *details = find_details(chan); >+ >+ if (!details) { >+ ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name); >+ ast_framehook_detach(chan, gateway->framehook); >+ return f; >+ } >+ >+ t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters); >+ ao2_ref(details, -1); >+ >+ if (!(fp = ast_frisolate(&control_frame))) { >+ ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name); >+ return f; >+ } >+ >+ gateway->t38_state = T38_STATE_NEGOTIATING; >+ gateway->timeout_start = ast_tvnow(); >+ details->gateway_timeout = FAX_GATEWAY_TIMEOUT; >+ >+ ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name); >+ return fp; >+} >+ >+static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f) >+{ >+ struct ast_frame *dfr = ast_frdup(f); >+ struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp; >+ struct ast_channel *other = (active == chan) ? peer : chan; >+ >+ if (gateway->detected_v21) { >+ return f; >+ } >+ >+ if (!dfr) { >+ return f; >+ } >+ >+ if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) { >+ return f; >+ } >+ >+ if (dfr->frametype == AST_FRAME_DTMF && ((dfr->subclass.integer == 'e') || (dfr->subclass.integer == 'g'))) { >+ gateway->detected_v21 = 1; >+ if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) { >+ ast_debug(1, "detected v21 preamble from %s\n", active->name); >+ return fax_gateway_request_t38(gateway, chan, f); >+ } else { >+ ast_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name); >+ } >+ } >+ >+ ast_frfree(dfr); >+ return f; >+} >+ >+static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel *active, struct ast_control_t38_parameters *control_params) >+{ >+ if (active == chan) { >+ return ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params)); >+ } else { >+ return ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params)); >+ } >+} >+ >+/*! \brief T38 Gateway Negotiate t38 parameters >+ * \param gateway gateway object >+ * \param chan channel running the gateway >+ * \param peer channel im bridged too >+ * \param active channel the frame originated on >+ * \param f the control frame to process >+ * \return processed control frame or null frame >+ */ >+static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f) >+{ >+ struct ast_control_t38_parameters *control_params = f->data.ptr; >+ struct ast_channel *other = (active == chan) ? peer : chan; >+ struct ast_fax_session_details *details; >+ >+ if (f->datalen != sizeof(struct ast_control_t38_parameters)) { >+ /* invalaid AST_CONTROL_T38_PARAMETERS frame, we can't >+ * do anything with it, pass it on */ >+ return f; >+ } >+ >+ /* ignore frames from ourselves */ >+ if ((gateway->t38_state == T38_STATE_NEGOTIATED && control_params->request_response == AST_T38_NEGOTIATED) >+ || (gateway->t38_state == T38_STATE_REJECTED && control_params->request_response == AST_T38_REFUSED) >+ || (gateway->t38_state == T38_STATE_NEGOTIATING && control_params->request_response == AST_T38_REQUEST_TERMINATE)) { >+ >+ return f; >+ } >+ >+ if (!(details = find_details(chan))) { >+ ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name); >+ ast_framehook_detach(chan, gateway->framehook); >+ return f; >+ } >+ >+ if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) { >+ enum ast_t38_state state = ast_channel_get_t38_state(other); >+ >+ if (state == T38_STATE_UNKNOWN) { >+ /* we detected a request to negotiate T.38 and the >+ * other channel appears to support T.38, we'll pass >+ * the request through and only step in if the other >+ * channel rejects the request */ >+ ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", active->name, other->name); >+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); >+ gateway->t38_state = T38_STATE_UNKNOWN; >+ gateway->timeout_start = ast_tvnow(); >+ details->gateway_timeout = FAX_GATEWAY_TIMEOUT; >+ ao2_ref(details, -1); >+ return f; >+ } else if (state == T38_STATE_UNAVAILABLE || state == T38_STATE_REJECTED) { >+ /* the other channel does not support T.38, we need to >+ * step in here */ >+ ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", active->name, other->name); >+ ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); >+ >+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); >+ t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters); >+ >+ if (fax_gateway_start(gateway, details, chan)) { >+ ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); >+ gateway->t38_state = T38_STATE_REJECTED; >+ control_params->request_response = AST_T38_REFUSED; >+ >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ } else { >+ gateway->t38_state = T38_STATE_NEGOTIATED; >+ control_params->request_response = AST_T38_NEGOTIATED; >+ report_fax_status(chan, details, "T.38 Negotiated"); >+ } >+ >+ fax_gateway_indicate_t38(chan, active, control_params); >+ >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } else if (gateway->t38_state == T38_STATE_NEGOTIATING) { >+ /* we got a request to negotiate T.38 after we already >+ * sent one to the other party based on v21 preamble >+ * detection. We'll just pretend we passed this request >+ * through in the first place. */ >+ >+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); >+ gateway->t38_state = T38_STATE_UNKNOWN; >+ gateway->timeout_start = ast_tvnow(); >+ details->gateway_timeout = FAX_GATEWAY_TIMEOUT; >+ >+ ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble detection\n", active->name); >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } else if (gateway->t38_state == T38_STATE_NEGOTIATED) { >+ /* we got a request to negotiate T.38 after we already >+ * sent one to the other party based on v21 preamble >+ * detection and received a response. We need to >+ * respond to this and shut down the gateway. */ >+ >+ t38_parameters_fax_to_ast(control_params, &details->their_t38_parameters); >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ >+ control_params->request_response = AST_T38_NEGOTIATED; >+ >+ fax_gateway_indicate_t38(chan, active, control_params); >+ >+ ast_string_field_set(details, result, "SUCCESS"); >+ ast_string_field_set(details, resultstr, "no gateway necessary"); >+ ast_string_field_set(details, error, "NATIVE_T38"); >+ set_channel_variables(chan, details); >+ >+ ast_debug(1, "%s is attempting to negotiate T.38 after we already negotiated T.38 with %s, disabling the gateway\n", active->name, other->name); >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } else { >+ ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", active->name, other->name); >+ ao2_ref(details, -1); >+ return f; >+ } >+ } else if (gateway->t38_state == T38_STATE_NEGOTIATING >+ && control_params->request_response == AST_T38_REFUSED) { >+ >+ ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", active->name); >+ >+ /* our request to negotiate T.38 was refused, if the other >+ * channel supports T.38, they might still reinvite and save >+ * the day. Otherwise disable the gateway. */ >+ if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) { >+ gateway->t38_state = T38_STATE_UNAVAILABLE; >+ } else { >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "unable to negotiate T.38"); >+ ast_string_field_set(details, error, "T38_NEG_ERROR"); >+ set_channel_variables(chan, details); >+ } >+ >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } else if (gateway->t38_state == T38_STATE_NEGOTIATING >+ && control_params->request_response == AST_T38_NEGOTIATED) { >+ >+ ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); >+ >+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); >+ >+ if (fax_gateway_start(gateway, details, chan)) { >+ ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); >+ gateway->t38_state = T38_STATE_NEGOTIATING; >+ control_params->request_response = AST_T38_REQUEST_TERMINATE; >+ >+ fax_gateway_indicate_t38(chan, active, control_params); >+ } else { >+ gateway->t38_state = T38_STATE_NEGOTIATED; >+ report_fax_status(chan, details, "T.38 Negotiated"); >+ } >+ >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } else if (control_params->request_response == AST_T38_REFUSED) { >+ /* the other channel refused the request to negotiate T.38, >+ * we'll step in here and pretend the request was accepted */ >+ >+ ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", other->name, active->name); >+ ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", other->name, active->name); >+ >+ t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters); >+ >+ if (fax_gateway_start(gateway, details, chan)) { >+ ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); >+ gateway->t38_state = T38_STATE_REJECTED; >+ control_params->request_response = AST_T38_REFUSED; >+ >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ } else { >+ gateway->t38_state = T38_STATE_NEGOTIATED; >+ control_params->request_response = AST_T38_NEGOTIATED; >+ } >+ >+ ao2_ref(details, -1); >+ return f; >+ } else if (control_params->request_response == AST_T38_REQUEST_TERMINATE) { >+ /* the channel wishes to end our short relationship, we shall >+ * oblige */ >+ >+ ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", active->name); >+ >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ >+ gateway->t38_state = T38_STATE_REJECTED; >+ control_params->request_response = AST_T38_TERMINATED; >+ >+ fax_gateway_indicate_t38(chan, active, control_params); >+ >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } else if (control_params->request_response == AST_T38_NEGOTIATED) { >+ ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", active->name, other->name); >+ >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ >+ ast_string_field_set(details, result, "SUCCESS"); >+ ast_string_field_set(details, resultstr, "no gateway necessary"); >+ ast_string_field_set(details, error, "NATIVE_T38"); >+ set_channel_variables(chan, details); >+ >+ ao2_ref(details, -1); >+ return f; >+ } else if (control_params->request_response == AST_T38_TERMINATED) { >+ ast_debug(1, "T.38 disabled on channel %s\n", active->name); >+ >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } >+ >+ ao2_ref(details, -1); >+ return f; >+} >+ >+/*! \brief Destroy the gateway data structure when the framehook is detached >+ * \param data framehook data (gateway data)*/ >+static void fax_gateway_framehook_destroy(void *data) { >+ struct fax_gateway *gateway = data; >+ >+ if (gateway->s) { >+ switch (gateway->s->state) { >+ case AST_FAX_STATE_INITIALIZED: >+ case AST_FAX_STATE_OPEN: >+ case AST_FAX_STATE_ACTIVE: >+ case AST_FAX_STATE_COMPLETE: >+ if (gateway->s->tech->cancel_session) { >+ gateway->s->tech->cancel_session(gateway->s); >+ } >+ /* fall through */ >+ default: >+ break; >+ } >+ } >+ >+ ao2_ref(gateway, -1); >+} >+ >+/*! \brief T.30<->T.38 gateway framehook. >+ * >+ * Intercept packets on bridged channels and determine if a T.38 gateway is >+ * required. If a gateway is required, start a gateway and handle T.38 >+ * negotiation if necessary. >+ * >+ * \param chan channel running the gateway >+ * \param f frame to handle may be NULL >+ * \param event framehook event >+ * \param data framehook data (struct fax_gateway *) >+ * >+ * \return processed frame or NULL when f is NULL or a null frame >+ */ >+static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) { >+ struct fax_gateway *gateway = data; >+ struct ast_channel *peer, *active; >+ struct ast_fax_session_details *details; >+ >+ if (gateway->s) { >+ details = gateway->s->details; >+ ao2_ref(details, 1); >+ } else { >+ if (!(details = find_details(chan))) { >+ ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name); >+ ast_framehook_detach(chan, gateway->framehook); >+ details->gateway_id = -1; >+ return f; >+ } >+ } >+ >+ /* restore audio formats when we are detached */ >+ if (event == AST_FRAMEHOOK_EVENT_DETACHED) { >+ set_channel_variables(chan, details); >+ >+ if (gateway->bridged) { >+ ast_set_read_format(chan, gateway->chan_read_format); >+ ast_set_read_format(chan, gateway->chan_write_format); >+ >+ if ((peer = ast_bridged_channel(chan))) { >+ ast_set_read_format(peer, gateway->peer_read_format); >+ ast_set_read_format(peer, gateway->peer_write_format); >+ ast_channel_make_compatible(chan, peer); >+ } >+ } >+ >+ ao2_ref(details, -1); >+ return NULL; >+ } >+ >+ if (!f || (event == AST_FRAMEHOOK_EVENT_ATTACHED)) { >+ ao2_ref(details, -1); >+ return NULL; >+ }; >+ >+ /* this frame was generated by the fax gateway, pass it on */ >+ if (ast_test_flag(f, AST_FAX_FRFLAG_GATEWAY)) { >+ ao2_ref(details, -1); >+ return f; >+ } >+ >+ if (!(peer = ast_bridged_channel(chan))) { >+ /* not bridged, don't do anything */ >+ ao2_ref(details, -1); >+ return f; >+ } >+ >+ if (!gateway->bridged && peer) { >+ /* don't start a gateway if neither channel can handle T.38 */ >+ if (ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE && ast_channel_get_t38_state(peer) == T38_STATE_UNAVAILABLE) { >+ ast_debug(1, "not starting gateway for %s and %s; neither channel supports T.38\n", chan->name, peer->name); >+ ast_framehook_detach(chan, gateway->framehook); >+ details->gateway_id = -1; >+ >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "neither channel supports T.38"); >+ ast_string_field_set(details, error, "T38_NEG_ERROR"); >+ set_channel_variables(chan, details); >+ ao2_ref(details, -1); >+ return f; >+ } >+ >+ if (details->gateway_timeout) { >+ gateway->timeout_start = ast_tvnow(); >+ } >+ >+ /* we are bridged, change r/w formats to SLIN for v21 preamble >+ * detection and T.30 */ >+ gateway->chan_read_format = chan->readformat; >+ gateway->chan_write_format = chan->readformat; >+ >+ gateway->peer_read_format = peer->readformat; >+ gateway->peer_write_format = peer->readformat; >+ >+ ast_set_read_format(chan, AST_FORMAT_SLINEAR); >+ ast_set_write_format(chan, AST_FORMAT_SLINEAR); >+ >+ ast_set_read_format(peer, AST_FORMAT_SLINEAR); >+ ast_set_write_format(peer, AST_FORMAT_SLINEAR); >+ >+ ast_channel_make_compatible(chan, peer); >+ gateway->bridged = 1; >+ } >+ >+ if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) { >+ if (ast_tvdiff_ms(ast_tvnow(), gateway->timeout_start) > details->gateway_timeout) { >+ ast_debug(1, "no fax activity between %s and %s after %d ms, disabling gateway\n", chan->name, peer->name, details->gateway_timeout); >+ ast_framehook_detach(chan, gateway->framehook); >+ details->gateway_id = -1; >+ >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_build(details, resultstr, "no fax activity after %d ms", details->gateway_timeout); >+ ast_string_field_set(details, error, "TIMEOUT"); >+ set_channel_variables(chan, details); >+ ao2_ref(details, -1); >+ return f; >+ } >+ } >+ >+ /* only handle VOICE, MODEM, and CONTROL frames*/ >+ switch (f->frametype) { >+ case AST_FRAME_VOICE: >+ switch (f->subclass.codec) { >+ case AST_FORMAT_SLINEAR: >+ case AST_FORMAT_ALAW: >+ case AST_FORMAT_ULAW: >+ break; >+ default: >+ ao2_ref(details, -1); >+ return f; >+ } >+ break; >+ case AST_FRAME_MODEM: >+ if (f->subclass.integer == AST_MODEM_T38) { >+ break; >+ } >+ ao2_ref(details, -1); >+ return f; >+ case AST_FRAME_CONTROL: >+ if (f->subclass.integer == AST_CONTROL_T38_PARAMETERS) { >+ break; >+ } >+ ao2_ref(details, -1); >+ return f; >+ default: >+ ao2_ref(details, -1); >+ return f; >+ } >+ >+ /* detect the active channel */ >+ switch (event) { >+ case AST_FRAMEHOOK_EVENT_WRITE: >+ active = peer; >+ break; >+ case AST_FRAMEHOOK_EVENT_READ: >+ active = chan; >+ break; >+ default: >+ ast_log(LOG_WARNING, "unhandled framehook event %i\n", event); >+ ao2_ref(details, -1); >+ return f; >+ } >+ >+ /* handle control frames */ >+ if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) { >+ ao2_ref(details, -1); >+ return fax_gateway_detect_t38(gateway, chan, peer, active, f); >+ } >+ >+ if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) { >+ /* not in gateway mode and have not detected v21 yet, listen >+ * for v21 */ >+ ao2_ref(details, -1); >+ return fax_gateway_detect_v21(gateway, chan, peer, active, f); >+ } >+ >+ /* in gateway mode, gateway some packets */ >+ if (gateway->t38_state == T38_STATE_NEGOTIATED) { >+ /* framehooks are called in __ast_read() before frame format >+ * translation is done, so we need to translate here */ >+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec != AST_FORMAT_SLINEAR)) { >+ if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) { >+ f = &ast_null_frame; >+ ao2_ref(details, -1); >+ return f; >+ } >+ } >+ >+ /* XXX we ignore the return value here, perhaps we should >+ * disable the gateway if a write fails. I am not sure how a >+ * write would fail, or even if a failure would be fatal so for >+ * now we'll just ignore the return value. */ >+ gateway->s->tech->write(gateway->s, f); >+ f = &ast_null_frame; >+ ao2_ref(details, -1); >+ return f; >+ } >+ >+ /* force silence on the line if T.38 negotiation might be taking place */ >+ if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) { >+ if (f->frametype == AST_FRAME_VOICE && f->subclass.codec == AST_FORMAT_SLINEAR) { >+ short silence_buf[f->samples]; >+ struct ast_frame silence_frame = { >+ .frametype = AST_FRAME_VOICE, >+ .data.ptr = silence_buf, >+ .samples = f->samples, >+ .datalen = sizeof(silence_buf), >+ .subclass.codec = AST_FORMAT_SLINEAR, >+ }; >+ memset(silence_buf, 0, sizeof(silence_buf)); >+ >+ ao2_ref(details, -1); >+ return ast_frisolate(&silence_frame); >+ } else { >+ ao2_ref(details, -1); >+ return &ast_null_frame; >+ } >+ } >+ >+ ao2_ref(details, -1); >+ return f; >+} >+ >+/*! \brief Attach a gateway framehook object to a channel. >+ * \param chan the channel to attach to >+ * \param details fax session details >+ * \return the framehook id of the attached framehook or -1 on error >+ * \retval -1 error >+ */ >+static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details) >+{ >+ struct fax_gateway *gateway; >+ struct ast_framehook_interface fr_hook = { >+ .version = AST_FRAMEHOOK_INTERFACE_VERSION, >+ .event_cb = fax_gateway_framehook, >+ .destroy_cb = fax_gateway_framehook_destroy, >+ }; >+ >+ ast_string_field_set(details, result, "SUCCESS"); >+ ast_string_field_set(details, resultstr, "gateway operation started successfully"); >+ ast_string_field_set(details, error, "NO_ERROR"); >+ set_channel_variables(chan, details); >+ >+ /* set up the frame hook*/ >+ gateway = fax_gateway_new(details); >+ if (!gateway) { >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "error initializing gateway session"); >+ ast_string_field_set(details, error, "INIT_ERROR"); >+ set_channel_variables(chan, details); >+ report_fax_status(chan, details, "No Available Resource"); >+ return -1; >+ } >+ >+ fr_hook.data = gateway; >+ ast_channel_lock(chan); >+ gateway->framehook = ast_framehook_attach(chan, &fr_hook); >+ ast_channel_unlock(chan); >+ >+ if (gateway->framehook < 0) { >+ ao2_ref(gateway, -1); >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "error attaching gateway to channel"); >+ ast_string_field_set(details, error, "INIT_ERROR"); >+ set_channel_variables(chan, details); >+ return -1; >+ } >+ >+ return gateway->framehook; >+} >+ >+/*! \brief Faxdetect loop used by WaitFAX >+ * \details Run DSP faxdetect on the channel for timeout seconds or until fax is detected >+ * \param chan channel to run fax detect on >+ * \param timeout maximum time to wait for fax detection >+ * \return 0 if nothing was detected 1 on CNG detected 2 if T38 negotiation is started -1 on error*/ >+static int do_waitfax_exec(struct ast_channel *chan, int timeout, int noiselim) >+{ >+ int timeleft = timeout; >+ int res = 0; >+ int dspnoise = 0; >+ struct ast_dsp *dsp = NULL; >+ struct ast_frame *f, *dup_f; >+ enum ast_t38_state t38state = T38_STATE_UNKNOWN; >+ unsigned int orig_read_format; >+ AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; >+ >+ /* Setup DSP CNG processing */ >+ orig_read_format = chan->readformat; >+ switch (chan->readformat) { >+ case AST_FORMAT_SLINEAR: >+ break; >+ default: >+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { >+ return -1; >+ } >+ } >+ >+ if (!(dsp = ast_dsp_new())) { >+ return -1; >+ } >+ >+ ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT); >+ ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_SQUELCH); >+ ast_dsp_set_threshold(dsp, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE)); >+ >+ AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames); >+ >+ while ((timeleft = ast_waitfor(chan, timeleft))) { >+ if (!(f = ast_read(chan))) { >+ break; >+ } >+ >+ if (dsp && (f->frametype == AST_FRAME_VOICE) && ((f->subclass.codec == AST_FORMAT_SLINEAR) || >+ (f->subclass.codec == AST_FORMAT_ULAW) || (f->subclass.codec == AST_FORMAT_ALAW))) { >+ f = ast_dsp_process(chan, dsp, f); >+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == 'f')) { >+ res = 1; >+ } else if ((f->frametype == AST_FRAME_VOICE) && (noiselim > 0)) { >+ ast_dsp_noise(dsp, f, &dspnoise); >+ if (dspnoise > noiselim) { >+ break; >+ } >+ } >+ } >+ >+ if (ast_is_deferrable_frame(f)) { >+ AST_LIST_INSERT_HEAD(&deferred_frames, f, frame_list); >+ } else { >+ ast_frfree(f); >+ } >+ >+ t38state = ast_channel_get_t38_state(chan); >+ if ((t38state == T38_STATE_NEGOTIATING) || (t38state == T38_STATE_NEGOTIATED)) { >+ res = 2; >+ break; >+ } else if (res) { >+ break; >+ } >+ } >+ >+ if (orig_read_format != chan->readformat) { >+ ast_set_read_format(chan, orig_read_format); >+ } >+ >+ ast_channel_lock(chan); >+ while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) { >+ dup_f = ast_frisolate(f); >+ ast_queue_frame_head(chan, dup_f); >+ if (dup_f != f) { >+ ast_frfree(f); >+ } >+ } >+ ast_channel_unlock(chan); >+ >+ if (dsp) { >+ ast_dsp_free(dsp); >+ } >+ >+ return res; >+} >+ >+/*! \brief Alternate wait app that listens for CNG >+ * \details This app answers the channel and waits for CNG tone or T38 negotiation >+ * if the channel driver supports faxdetect it will proceed to the fax >+ * extension. >+ * it will set FAXOPT(status) and FAXOPT(statusstr) allowing dial plan processing where >+ * the channel does not have fax detect or the Dialplan needs to handle faxdetection >+ * >+ * \param chan channel this application is called on >+ * \param data the paramaters passed to the application >+ * \return this application will always return 0 >+ */ >+static int waitfax_exec(struct ast_channel *chan, const char *data) >+{ >+ int res = 0; >+ unsigned int timeout; >+ unsigned int noiselim; >+ int ptres = -1; >+ char *parse; >+ struct ast_fax_session_details *details; >+ struct ast_silence_generator *silgen = NULL; >+ struct ast_tone_zone_sound *ts; >+ >+ AST_DECLARE_APP_ARGS(args, >+ AST_APP_ARG(timeout); >+ AST_APP_ARG(tone); >+ AST_APP_ARG(noise); >+ ); >+ >+ if (!(details = find_or_create_details(chan))) { >+ ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); >+ return 0; >+ } >+ >+ parse = ast_strdupa(data); >+ AST_STANDARD_APP_ARGS(args, parse); >+ >+ if (!ast_strlen_zero(args.timeout) && sscanf(args.timeout, "%u", &timeout) == 1) { >+ timeout = timeout * 1000; >+ } else { >+ timeout = 0; >+ } >+ >+ if (timeout <= 0) { >+ ast_string_field_set(details, result, "ERROR"); >+ ast_string_field_set(details, resultstr, "Invalid timeout in WaitFAX"); >+ ast_log(LOG_ERROR, "Application WaitFAX requires a valid timeout\n"); >+ ao2_ref(details, -1); >+ return 0; >+ } >+ >+ if (ast_strlen_zero(args.noise) || sscanf(args.noise, "%u", &noiselim) != 1) { >+ noiselim = 0; >+ } >+ >+ if (chan->_state != AST_STATE_UP) { >+ ast_answer(chan); >+ } >+ >+ /* If no other generator is present, start tone or silencegen while waiting */ >+ if (!chan->generatordata && !ast_strlen_zero(args.tone)) { >+ if ((ts = ast_get_indication_tone(chan->zone, args.tone))) { >+ ptres = ast_playtones_start(chan, 0, ts->data, 0); >+ ts = ast_tone_zone_sound_unref(ts); >+ } else { >+ ptres = ast_playtones_start(chan, 0, args.tone, 0); >+ } >+ if (!ptres && ast_opt_transmit_silence) { >+ silgen = ast_channel_start_silence_generator(chan); >+ } >+ } else if (ast_opt_transmit_silence && !chan->generatordata) { >+ silgen = ast_channel_start_silence_generator(chan); >+ } >+ >+ res = do_waitfax_exec(chan, timeout, noiselim); >+ >+ /* stop silgen or tones if present */ >+ if (silgen) { >+ ast_channel_stop_silence_generator(chan, silgen); >+ } else if (!ptres) { >+ ast_playtones_stop(chan); >+ } >+ >+ if (res > 0) { >+ ast_string_field_set(details, result, "SUCCESS"); >+ if (res == 1) { >+ ast_string_field_set(details, resultstr, "CNG"); >+ } else { >+ ast_string_field_set(details, resultstr, "T38"); >+ } >+ } else if (res < 0) { >+ ast_string_field_set(details, result, "ERROR"); >+ ast_string_field_set(details, resultstr, "DSP Error WaitFAX Failed"); >+ } else { >+ ast_string_field_set(details, result, "FAILED"); >+ ast_string_field_set(details, resultstr, "No CNG Tone Or T38 Detected"); >+ } >+ >+ ao2_ref(details, -1); >+ return 0; >+} >+ >+ > /*! \brief hash callback for ao2 */ > static int session_hash_cb(const void *obj, const int flags) > { >@@ -2558,19 +3619,15 @@ > while ((s = ao2_iterator_next(&i))) { > ao2_lock(s); > >- if (!(filenames = generate_filenames_string(s->details, "", ", "))) { >- ast_log(LOG_ERROR, "error printing filenames for 'fax show sessions' command"); >- ao2_unlock(s); >- ao2_ref(s, -1); >- ao2_iterator_destroy(&i); >- return CLI_FAILURE; >- } >+ filenames = generate_filenames_string(s->details, "", ", "); > > ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n", > s->channame, s->tech->type, s->id, > (s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38", >- (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", >- ast_fax_state_to_str(s->state), filenames); >+ (s->details->caps & AST_FAX_TECH_GATEWAY) >+ ? "gateway" >+ : (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", >+ ast_fax_state_to_str(s->state), S_OR(filenames, "")); > > ast_free(filenames); > ao2_unlock(s); >@@ -2689,6 +3746,9 @@ > } > if (!strcasecmp(data, "ecm")) { > ast_copy_string(buf, details->option.ecm ? "yes" : "no", len); >+ } else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") || >+ !strcasecmp(data, "t38_gateway") || !strcasecmp(data, "faxgateway")) { >+ ast_copy_string(buf, details->gateway_id != -1 ? "yes" : "no", len); > } else if (!strcasecmp(data, "error")) { > ast_copy_string(buf, details->error, len); > } else if (!strcasecmp(data, "filename")) { >@@ -2763,6 +3823,43 @@ > } else { > ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(ecm).\n", value); > } >+ } else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") || >+ !strcasecmp(data, "t38_gateway") || !strcasecmp(data, "faxgateway")) { >+ const char *val = ast_skip_blanks(value); >+ char *timeout = strchr(val, ','); >+ >+ if (timeout) { >+ *timeout++ = '\0'; >+ } >+ >+ if (ast_true(val)) { >+ if (details->gateway_id < 0) { >+ details->gateway_timeout = 0; >+ if (timeout) { >+ unsigned int gwtimeout; >+ if (sscanf(timeout, "%u", &gwtimeout) == 1) { >+ details->gateway_timeout = gwtimeout * 1000; >+ } else { >+ ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n", timeout, data); >+ } >+ } >+ >+ details->gateway_id = fax_gateway_attach(chan, details); >+ if (details->gateway_id < 0) { >+ ast_log(LOG_ERROR, "Error attaching T.38 gateway to channel %s.\n", chan->name); >+ res = -1; >+ } else { >+ ast_debug(1, "Attached T.38 gateway to channel %s.\n", chan->name); >+ } >+ } else { >+ ast_log(LOG_WARNING, "Attempt to attach a T.38 gateway on channel (%s) with gateway already running.\n", chan->name); >+ } >+ } else if (ast_false(val)) { >+ ast_framehook_detach(chan, details->gateway_id); >+ details->gateway_id = -1; >+ } else { >+ ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data); >+ } > } else if (!strcasecmp(data, "headerinfo")) { > ast_string_field_set(details, headerinfo, value); > } else if (!strcasecmp(data, "localstationid")) { >@@ -2813,6 +3910,10 @@ > ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_receivefax); > } > >+ if (ast_unregister_application(app_waitfax) < 0) { >+ ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_waitfax); >+ } >+ > if (fax_logger_level != -1) { > ast_logger_unregister_level("FAX"); > } >@@ -2852,8 +3953,13 @@ > ao2_ref(faxregistry.container, -1); > return AST_MODULE_LOAD_DECLINE; > } >+ >+ if (ast_register_application_xml(app_waitfax, waitfax_exec) < 0) { >+ ast_log(LOG_WARNING, "failed to register '%s'.\n", app_waitfax); >+ } >+ > ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli)); >- res = ast_custom_function_register(&acf_faxopt); >+ res = ast_custom_function_register(&acf_faxopt); > fax_logger_level = ast_logger_register_level("FAX"); > > return res; >Index: res/res_fax_spandsp.c >=================================================================== >--- res/res_fax_spandsp.c (.../branches/1.8) (revision 336160) >+++ res/res_fax_spandsp.c (.../team/irroot/t38gateway-1.8) (revision 336160) >@@ -5,6 +5,22 @@ > * > * Matthew Nicholson <mnicholson@digium.com> > * >+ * Initial T.38-gateway code >+ * 2008, Daniel Ferenci <daniel.ferenci@nethemba.com> >+ * Created by Nethemba s.r.o. http://www.nethemba.com >+ * Sponsored by IPEX a.s. http://www.ipex.cz >+ * >+ * T.38-gateway integration into asterisk app_fax and rework >+ * 2008, Gregory Hinton Nietsky <gregory@dnstelecom.co.za> >+ * dns Telecom http://www.dnstelecom.co.za >+ * >+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2 >+ * 2010, Anton Verevkin <mymail@verevkin.it> >+ * ViaNetTV http://www.vianettv.com >+ * >+ * Modified to make T.38-gateway work >+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at >+ * > * See http://www.asterisk.org for more information about > * the Asterisk project. Please do not directly contact > * any of the maintainers of this project for assistance; >@@ -21,6 +37,7 @@ > * \brief Spandsp T.38 and G.711 FAX Resource > * > * \author Matthew Nicholson <mnicholson@digium.com> >+ * \author Gregory H. Nietsky <gregory@distrotech.co.za> > * > * This module registers the Spandsp FAX technology with the res_fax module. > */ >@@ -47,9 +64,11 @@ > #include "asterisk/timing.h" > #include "asterisk/astobj2.h" > #include "asterisk/res_fax.h" >+#include "asterisk/channel.h" > > #define SPANDSP_FAX_SAMPLES 160 > #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */ >+#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3 > > static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token); > static void spandsp_fax_destroy(struct ast_fax_session *s); >@@ -58,6 +77,9 @@ > static int spandsp_fax_start(struct ast_fax_session *s); > static int spandsp_fax_cancel(struct ast_fax_session *s); > static int spandsp_fax_switch_to_t38(struct ast_fax_session *s); >+static int spandsp_fax_gateway_start(struct ast_fax_session *s); >+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f); >+static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s); > > static char *spandsp_fax_cli_show_capabilities(int fd); > static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd); >@@ -76,7 +98,7 @@ > */ > .version = "pre-20090220", > #endif >- .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE, >+ .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY, > .new_session = spandsp_fax_new, > .destroy_session = spandsp_fax_destroy, > .read = spandsp_fax_read, >@@ -115,6 +137,7 @@ > struct spandsp_pvt { > unsigned int ist38:1; > unsigned int isdone:1; >+ enum ast_t38_state ast_t38_state; > fax_state_t fax_state; > t38_terminal_state_t t38_state; > t30_state_t *t30_state; >@@ -122,6 +145,9 @@ > > struct spandsp_fax_stats *stats; > >+ struct spandsp_fax_gw_stats *t38stats; >+ t38_gateway_state_t t38_gw_state; >+ > struct ast_timer *timer; > AST_LIST_HEAD(frame_queue, ast_frame) read_frames; > }; >@@ -159,7 +185,9 @@ > */ > static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count) > { >- struct spandsp_pvt *p = data; >+ int res = -1; >+ struct ast_fax_session *s = data; >+ struct spandsp_pvt *p = s->tech_pvt; > struct ast_frame fax_frame = { > .frametype = AST_FRAME_MODEM, > .subclass.integer = AST_MODEM_T38, >@@ -175,13 +203,23 @@ > AST_FRAME_SET_BUFFER(f, buf, 0, len); > > if (!(f = ast_frisolate(f))) { >- return -1; >+ return res; > } > >- /* no need to lock, this all runs in the same thread */ >- AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list); >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ ast_set_flag(f, AST_FAX_FRFLAG_GATEWAY); >+ if (p->ast_t38_state == T38_STATE_NEGOTIATED) { >+ res = ast_write(s->chan, f); >+ } else { >+ res = ast_queue_frame(s->chan, f); >+ } >+ ast_frfree(f); >+ } else { >+ /* no need to lock, this all runs in the same thread */ >+ AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list); >+ } > >- return 0; >+ return res; > } > > static int update_stats(struct spandsp_pvt *p, int completion_code) >@@ -423,6 +461,11 @@ > goto e_return; > } > >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ s->state = AST_FAX_STATE_INITIALIZED; >+ return p; >+ } >+ > AST_LIST_HEAD_INIT(&p->read_frames); > > if (s->details->caps & AST_FAX_TECH_RECEIVE) { >@@ -451,7 +494,7 @@ > } > > /* init t38 stuff */ >- t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p); >+ t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, s); > set_logging(&p->t38_state.logging, s->details); > } > >@@ -476,7 +519,12 @@ > { > struct spandsp_pvt *p = s->tech_pvt; > >- session_destroy(p); >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ spandsp_fax_gateway_cleanup(s); >+ } else { >+ session_destroy(p); >+ } >+ > ast_free(p); > s->tech_pvt = NULL; > s->fd = -1; >@@ -496,7 +544,6 @@ > .subclass.codec = AST_FORMAT_SLINEAR, > .src = "res_fax_spandsp_g711", > }; >- > struct ast_frame *f = &fax_frame; > > ast_timer_ack(p->timer, 1); >@@ -538,6 +585,10 @@ > { > struct spandsp_pvt *p = s->tech_pvt; > >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ return spandsp_fax_gateway_process(s, f); >+ } >+ > /* XXX do we need to lock here? */ > if (s->state == AST_FAX_STATE_COMPLETE) { > ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state)); >@@ -551,6 +602,189 @@ > } > } > >+/*! \brief generate T.30 packets sent to the T.30 leg of gateway >+ * \param chan T.30 channel >+ * \param data fax session structure >+ * \param len not used >+ * \param samples no of samples generated >+ * \return -1 on failure or 0 on sucess*/ >+static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len, int samples) >+{ >+ int res = -1; >+ struct ast_fax_session *s = data; >+ struct spandsp_pvt *p = s->tech_pvt; >+ uint8_t buffer[AST_FRIENDLY_OFFSET + samples * sizeof(uint16_t)]; >+ struct ast_frame *f; >+ struct ast_frame t30_frame = { >+ .frametype = AST_FRAME_VOICE, >+ .subclass.codec = AST_FORMAT_SLINEAR, >+ .src = "res_fax_spandsp_g711", >+ .samples = samples, >+ .flags = AST_FAX_FRFLAG_GATEWAY, >+ }; >+ >+ AST_FRAME_SET_BUFFER(&t30_frame, buffer, AST_FRIENDLY_OFFSET, t30_frame.samples * sizeof(int16_t)); >+ >+ if (!(f = ast_frisolate(&t30_frame))) { >+ return p->isdone ? -1 : res; >+ } >+ >+ /* generate a T.30 packet */ >+ if ((f->samples = t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples))) { >+ f->datalen = f->samples * sizeof(int16_t); >+ res = ast_write(chan, f); >+ } >+ ast_frfree(f); >+ return p->isdone ? -1 : res; >+} >+ >+/*! \brief simple routine to allocate data to generator >+ * \param chan channel >+ * \param params generator data >+ * \return data to use in generator call*/ >+static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params) { >+ ao2_ref(params, +1); >+ return params; >+} >+ >+static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data) { >+ ao2_ref(data, -1); >+} >+ >+/*! \brief activate a spandsp gateway based on the information in the given fax session >+ * \param s fax session >+ * \return -1 on error 0 on sucess*/ >+static int spandsp_fax_gateway_start(struct ast_fax_session *s) { >+ struct spandsp_pvt *p = s->tech_pvt; >+ struct ast_fax_t38_parameters *t38_param; >+ int i, modems = 0; >+ struct ast_channel *peer; >+ static struct ast_generator t30_gen = { >+ alloc: spandsp_fax_gw_gen_alloc, >+ release: spandsp_fax_gw_gen_release, >+ generate: spandsp_fax_gw_t30_gen, >+ }; >+ >+#if SPANDSP_RELEASE_DATE >= 20081012 >+ /* for spandsp shaphots 0.0.6 and higher */ >+ p->t38_core_state=&p->t38_gw_state.t38x.t38; >+#else >+ /* for spandsp release 0.0.5 */ >+ p->t38_core_state=&p->t38_gw_state.t38; >+#endif >+ >+ if (!t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, s)) { >+ return -1; >+ } >+ >+ p->ist38 = 1; >+ p->ast_t38_state = ast_channel_get_t38_state(s->chan); >+ if (!(peer = ast_bridged_channel(s->chan))) { >+ ast_channel_unlock(s->chan); >+ return -1; >+ } >+ >+ /* we can be in T38_STATE_NEGOTIATING or T38_STATE_NEGOTIATED when the >+ * gateway is started. We treat both states the same. */ >+ if (p->ast_t38_state == T38_STATE_NEGOTIATING) { >+ p->ast_t38_state = T38_STATE_NEGOTIATED; >+ } >+ >+ ast_activate_generator(p->ast_t38_state == T38_STATE_NEGOTIATED ? peer : s->chan, &t30_gen , s); >+ >+ set_logging(&p->t38_gw_state.logging, s->details); >+ set_logging(&p->t38_core_state->logging, s->details); >+ >+ t38_param = (p->ast_t38_state == T38_STATE_NEGOTIATED) ? &s->details->our_t38_parameters : &s->details->their_t38_parameters; >+ t38_set_t38_version(p->t38_core_state, t38_param->version); >+ t38_gateway_set_ecm_capability(&p->t38_gw_state, s->details->option.ecm); >+ t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp); >+ t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal); >+ t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr); >+ t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig); >+ t38_set_data_rate_management_method(p->t38_core_state, >+ (t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2); >+ >+ t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE); >+ t38_set_sequence_number_handling(p->t38_core_state, TRUE); >+ >+ if (AST_FAX_MODEM_V17 & s->details->modems) { >+ modems |= T30_SUPPORT_V17; >+ } >+ if (AST_FAX_MODEM_V27 & s->details->modems) { >+ modems |= T30_SUPPORT_V27TER; >+ } >+ if (AST_FAX_MODEM_V29 & s->details->modems) { >+ modems |= T30_SUPPORT_V29; >+ } >+ if (AST_FAX_MODEM_V34 & s->details->modems) { >+#if defined(T30_SUPPORT_V34) >+ modems |= T30_SUPPORT_V34; >+#elif defined(T30_SUPPORT_V34HDX) >+ modems |= T30_SUPPORT_V34HDX; >+#else >+ ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n"); >+#endif >+ } >+ >+ t38_gateway_set_supported_modems(&p->t38_gw_state, modems); >+ >+ /* engage udptl nat on other side of T38 line >+ * (Asterisk changes media ports thus we send a few packets to reinitialize >+ * pinholes in NATs and FWs >+ */ >+ for (i=0; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) { >+#if SPANDSP_RELEASE_DATE >= 20091228 >+ t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL); >+#elif SPANDSP_RELEASE_DATE >= 20081012 >+ t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38x.t38.indicator_tx_count); >+#else >+ t38_core_send_indicator(&p->t38_gw_state.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38.indicator_tx_count); >+#endif >+ } >+ >+ s->state = AST_FAX_STATE_ACTIVE; >+ >+ return 0; >+} >+ >+/*! \brief process a frame from the bridge >+ * \param s fax session >+ * \param f frame to process >+ * \return 1 on sucess 0 on incorect packet*/ >+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f) >+{ >+ struct spandsp_pvt *p = s->tech_pvt; >+ >+ /*invalid frame*/ >+ if (!f->data.ptr || !f->datalen) { >+ return -1; >+ } >+ >+ /* Process a IFP packet */ >+ if ((f->frametype == AST_FRAME_MODEM) && (f->subclass.integer == AST_MODEM_T38)) { >+ return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno); >+ } else if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) { >+ return t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples); >+ } >+ >+ return -1; >+} >+ >+/*! \brief gather data and clean up after gateway ends >+ * \param s fax session*/ >+static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s) >+{ >+ struct spandsp_pvt *p = s->tech_pvt; >+ t38_stats_t t38_stats; >+ >+ t38_gateway_get_transfer_statistics(&p->t38_gw_state, &t38_stats); >+ >+ s->details->option.ecm = t38_stats.error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE; >+ s->details->pages_transferred = t38_stats.pages_transferred; >+ ast_string_field_build(s->details, transfer_rate, "%d", t38_stats.bit_rate); >+} >+ > /*! \brief */ > static int spandsp_fax_start(struct ast_fax_session *s) > { >@@ -558,6 +792,10 @@ > > s->state = AST_FAX_STATE_OPEN; > >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ return spandsp_fax_gateway_start(s); >+ } >+ > if (p->ist38) { > #if SPANDSP_RELEASE_DATE >= 20080725 > /* for spandsp shaphots 0.0.6 and higher */ >@@ -627,6 +865,12 @@ > static int spandsp_fax_cancel(struct ast_fax_session *s) > { > struct spandsp_pvt *p = s->tech_pvt; >+ >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ p->isdone = 1; >+ return 0; >+ } >+ > t30_terminate(p->t30_state); > p->isdone = 1; > return 0; >@@ -655,7 +899,7 @@ > /*! \brief */ > static char *spandsp_fax_cli_show_capabilities(int fd) > { >- ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n"); >+ ast_cli(fd, "SEND RECEIVE T.38 G.711 GATEWAY\n\n"); > return CLI_SUCCESS; > } > >@@ -663,35 +907,48 @@ > static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd) > { > struct spandsp_pvt *p = s->tech_pvt; >- t30_stats_t stats; > > ao2_lock(s); >- ast_cli(fd, "%-22s : %d\n", "session", s->id); >- ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit"); >- ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state)); >- if (s->state != AST_FAX_STATE_UNINITIALIZED) { >- t30_get_transfer_statistics(p->t30_state, &stats); >- ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status)); >- ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No"); >- ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate); >- ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution); >+ if (s->details->caps & AST_FAX_TECH_GATEWAY) { >+ ast_cli(fd, "%-22s : %d\n", "session", s->id); >+ ast_cli(fd, "%-22s : %s\n", "operation", "Gateway"); >+ ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state)); >+ if (s->state != AST_FAX_STATE_UNINITIALIZED) { >+ t38_stats_t stats; >+ t38_gateway_get_transfer_statistics(&p->t38_gw_state, &stats); >+ ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No"); >+ ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate); >+ ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1); >+ } >+ } else { >+ ast_cli(fd, "%-22s : %d\n", "session", s->id); >+ ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit"); >+ ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state)); >+ if (s->state != AST_FAX_STATE_UNINITIALIZED) { >+ t30_stats_t stats; >+ t30_get_transfer_statistics(p->t30_state, &stats); >+ ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status)); >+ ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No"); >+ ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate); >+ ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution); > #if SPANDSP_RELEASE_DATE >= 20090220 >- ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1); >+ ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1); > #else >- ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1); >+ ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1); > #endif >- ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file); >+ ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file); > >- ast_cli(fd, "\nData Statistics:\n"); >+ ast_cli(fd, "\nData Statistics:\n"); > #if SPANDSP_RELEASE_DATE >= 20090220 >- ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx); >- ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx); >+ ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx); >+ ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx); > #else >- ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0); >- ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0); >+ ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0); >+ ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0); > #endif >- ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run); >- ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows); >+ ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run); >+ ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows); >+ } > } > ao2_unlock(s); > ast_cli(fd, "\n\n"); > >Property changes on: . >___________________________________________________________________ >Added: automerge > + * >Added: svnmerge-integrated > + /branches/1.8:1-336111 >Added: automerge-email > + gregory@distrotech.co.za >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 383987
:
287313
|
287315
|
287317
|
287319
| 287321