|
Lines 5-11
Link Here
|
| 5 |
* |
5 |
* |
| 6 |
* Dwayne M. Hubbard <dhubbard@digium.com> |
6 |
* Dwayne M. Hubbard <dhubbard@digium.com> |
| 7 |
* Kevin P. Fleming <kpfleming@digium.com> |
7 |
* Kevin P. Fleming <kpfleming@digium.com> |
|
|
8 |
* Matthew Nicholson <mnicholson@digium.com> |
| 8 |
* |
9 |
* |
|
|
10 |
* Initial T.38-gateway code |
| 11 |
* 2008, Daniel Ferenci <daniel.ferenci@nethemba.com> |
| 12 |
* Created by Nethemba s.r.o. http://www.nethemba.com |
| 13 |
* Sponsored by IPEX a.s. http://www.ipex.cz |
| 14 |
* |
| 15 |
* T.38-gateway integration into asterisk app_fax and rework |
| 16 |
* 2008-2011, Gregory Hinton Nietsky <gregory@distrotech.co.za> |
| 17 |
* dns Telecom http://www.dnstelecom.co.za |
| 18 |
* |
| 19 |
* Modified to make T.38-gateway compatible with Asterisk 1.6.2 |
| 20 |
* 2010, Anton Verevkin <mymail@verevkin.it> |
| 21 |
* ViaNetTV http://www.vianettv.com |
| 22 |
* |
| 23 |
* Modified to make T.38-gateway work |
| 24 |
* 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at |
| 25 |
* |
| 9 |
* See http://www.asterisk.org for more information about |
26 |
* See http://www.asterisk.org for more information about |
| 10 |
* the Asterisk project. Please do not directly contact |
27 |
* the Asterisk project. Please do not directly contact |
| 11 |
* any of the maintainers of this project for assistance; |
28 |
* any of the maintainers of this project for assistance; |
|
Lines 27-32
Link Here
|
| 27 |
* |
44 |
* |
| 28 |
* \author Dwayne M. Hubbard <dhubbard@digium.com> |
45 |
* \author Dwayne M. Hubbard <dhubbard@digium.com> |
| 29 |
* \author Kevin P. Fleming <kpfleming@digium.com> |
46 |
* \author Kevin P. Fleming <kpfleming@digium.com> |
|
|
47 |
* \author Matthew Nicholson <mnicholson@digium.com> |
| 48 |
* \author Gregory H. Nietsky <gregory@distrotech.co.za> |
| 30 |
* |
49 |
* |
| 31 |
* A generic FAX resource module that provides SendFAX and ReceiveFAX applications. |
50 |
* A generic FAX resource module that provides SendFAX and ReceiveFAX applications. |
| 32 |
* This module requires FAX technology modules, like res_fax_spandsp, to register with it |
51 |
* This module requires FAX technology modules, like res_fax_spandsp, to register with it |
|
Lines 62-67
Link Here
|
| 62 |
#include "asterisk/dsp.h" |
81 |
#include "asterisk/dsp.h" |
| 63 |
#include "asterisk/indications.h" |
82 |
#include "asterisk/indications.h" |
| 64 |
#include "asterisk/ast_version.h" |
83 |
#include "asterisk/ast_version.h" |
|
|
84 |
#include "asterisk/translate.h" |
| 65 |
|
85 |
|
| 66 |
/*** DOCUMENTATION |
86 |
/*** DOCUMENTATION |
| 67 |
<application name="ReceiveFax" language="en_US"> |
87 |
<application name="ReceiveFax" language="en_US"> |
|
Lines 163-168
Link Here
|
| 163 |
<enum name="modem"> |
183 |
<enum name="modem"> |
| 164 |
<para>R/W Modem type (v17/v27/v29).</para> |
184 |
<para>R/W Modem type (v17/v27/v29).</para> |
| 165 |
</enum> |
185 |
</enum> |
|
|
186 |
<enum name="gateway"> |
| 187 |
<para>R/W T38 Gateway Enabled With optional fax activity timeout in seconds (yes[,gwtimeout]/no)</para> |
| 188 |
</enum> |
| 166 |
<enum name="pages"> |
189 |
<enum name="pages"> |
| 167 |
<para>R/O Number of pages transferred.</para> |
190 |
<para>R/O Number of pages transferred.</para> |
| 168 |
</enum> |
191 |
</enum> |
|
Lines 196-205
Link Here
|
| 196 |
<ref type="application">SendFax</ref> |
219 |
<ref type="application">SendFax</ref> |
| 197 |
</see-also> |
220 |
</see-also> |
| 198 |
</function> |
221 |
</function> |
|
|
222 |
<application name="WaitFAX" language="en_US"> |
| 223 |
<synopsis> |
| 224 |
Generic Fax Detect CNG/T.38 (Wait For Fax) |
| 225 |
</synopsis> |
| 226 |
<syntax> |
| 227 |
<parameter name="timeout" required="true"> |
| 228 |
<para>Specifies the number of seconds we attempt to detect a fax tone on the channel</para> |
| 229 |
</parameter> |
| 230 |
<parameter name="tone" required="false"> |
| 231 |
<para>Either the tone name defined in the "indications.conf" configuration file, |
| 232 |
or a directly specified list of frequencies and durations.</para> |
| 233 |
<para>If not specified silence is generated.</para> |
| 234 |
</parameter> |
| 235 |
<parameter name="noise" required="false"> |
| 236 |
<para>Number of ms noise detected before proceeding with the dialplan.</para> |
| 237 |
</parameter> |
| 238 |
</syntax> |
| 239 |
<description> |
| 240 |
<para>This application sets FAXOPT(status) To SUCCESS | FAILURE | ERROR</para> |
| 241 |
<para>FAXOPT(statusstr) will be set to CNG | T38 on SUCCESS or reason on FAILURE | ERROR</para> |
| 242 |
</description> |
| 243 |
</application> |
| 199 |
***/ |
244 |
***/ |
| 200 |
|
245 |
|
| 201 |
static const char app_receivefax[] = "ReceiveFAX"; |
246 |
static const char app_receivefax[] = "ReceiveFAX"; |
| 202 |
static const char app_sendfax[] = "SendFAX"; |
247 |
static const char app_sendfax[] = "SendFAX"; |
|
|
248 |
static const char app_waitfax[] = "WaitFAX"; |
| 203 |
|
249 |
|
| 204 |
struct debug_info_history { |
250 |
struct debug_info_history { |
| 205 |
unsigned int consec_frames; |
251 |
unsigned int consec_frames; |
|
Lines 213-224
Link Here
|
| 213 |
struct ast_dsp *dsp; |
259 |
struct ast_dsp *dsp; |
| 214 |
}; |
260 |
}; |
| 215 |
|
261 |
|
|
|
262 |
/*! \brief used for gateway framehook */ |
| 263 |
struct fax_gateway { |
| 264 |
/*! \brief FAX Session */ |
| 265 |
struct ast_fax_session *s; |
| 266 |
/*! \brief reserved fax session token */ |
| 267 |
struct ast_fax_tech_token *token; |
| 268 |
/*! \brief the start of our timeout counter */ |
| 269 |
struct timeval timeout_start; |
| 270 |
/*! \brief DSP Processor */ |
| 271 |
struct ast_dsp *chan_dsp; |
| 272 |
struct ast_dsp *peer_dsp; |
| 273 |
/*! \brief framehook used in gateway mode */ |
| 274 |
int framehook; |
| 275 |
/*! \brief bridged */ |
| 276 |
int bridged:1; |
| 277 |
/*! \brief 1 if a v21 preamble has been detected */ |
| 278 |
int detected_v21:1; |
| 279 |
/*! \brief a flag to track the state of our negotiation */ |
| 280 |
enum ast_t38_state t38_state; |
| 281 |
/*! \brief Original audio formats */ |
| 282 |
unsigned int chan_read_format; |
| 283 |
unsigned int chan_write_format; |
| 284 |
unsigned int peer_read_format; |
| 285 |
unsigned int peer_write_format; |
| 286 |
}; |
| 287 |
|
| 216 |
static int fax_logger_level = -1; |
288 |
static int fax_logger_level = -1; |
| 217 |
|
289 |
|
| 218 |
/*! \brief maximum buckets for res_fax ao2 containers */ |
290 |
/*! \brief maximum buckets for res_fax ao2 containers */ |
| 219 |
#define FAX_MAXBUCKETS 10 |
291 |
#define FAX_MAXBUCKETS 10 |
| 220 |
|
292 |
|
| 221 |
#define RES_FAX_TIMEOUT 10000 |
293 |
#define RES_FAX_TIMEOUT 10000 |
|
|
294 |
#define FAX_GATEWAY_TIMEOUT RES_FAX_TIMEOUT |
| 222 |
|
295 |
|
| 223 |
/*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */ |
296 |
/*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */ |
| 224 |
static struct { |
297 |
static struct { |
|
Lines 393-402
Link Here
|
| 393 |
d->modems = general_options.modems; |
466 |
d->modems = general_options.modems; |
| 394 |
d->minrate = general_options.minrate; |
467 |
d->minrate = general_options.minrate; |
| 395 |
d->maxrate = general_options.maxrate; |
468 |
d->maxrate = general_options.maxrate; |
|
|
469 |
d->gateway_id = -1; |
| 396 |
|
470 |
|
| 397 |
return d; |
471 |
return d; |
| 398 |
} |
472 |
} |
| 399 |
|
473 |
|
|
|
474 |
static struct ast_control_t38_parameters our_t38_parameters = { |
| 475 |
.version = 0, |
| 476 |
.max_ifp = 400, |
| 477 |
.rate = AST_T38_RATE_14400, |
| 478 |
.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, |
| 479 |
}; |
| 480 |
|
| 481 |
static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src) |
| 482 |
{ |
| 483 |
dst->version = src->version; |
| 484 |
dst->max_ifp = src->max_ifp; |
| 485 |
dst->rate = src->rate; |
| 486 |
dst->rate_management = src->rate_management; |
| 487 |
dst->fill_bit_removal = src->fill_bit_removal; |
| 488 |
dst->transcoding_mmr = src->transcoding_mmr; |
| 489 |
dst->transcoding_jbig = src->transcoding_jbig; |
| 490 |
} |
| 491 |
|
| 492 |
static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src) |
| 493 |
{ |
| 494 |
dst->version = src->version; |
| 495 |
dst->max_ifp = src->max_ifp; |
| 496 |
dst->rate = src->rate; |
| 497 |
dst->rate_management = src->rate_management; |
| 498 |
dst->fill_bit_removal = src->fill_bit_removal; |
| 499 |
dst->transcoding_mmr = src->transcoding_mmr; |
| 500 |
dst->transcoding_jbig = src->transcoding_jbig; |
| 501 |
} |
| 502 |
|
| 400 |
/*! \brief returns a reference counted details structure from the channel's fax datastore. If the datastore |
503 |
/*! \brief returns a reference counted details structure from the channel's fax datastore. If the datastore |
| 401 |
* does not exist it will be created */ |
504 |
* does not exist it will be created */ |
| 402 |
static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan) |
505 |
static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan) |
|
Lines 419-424
Link Here
|
| 419 |
} |
522 |
} |
| 420 |
/* add the datastore to the channel and increment the refcount */ |
523 |
/* add the datastore to the channel and increment the refcount */ |
| 421 |
datastore->data = details; |
524 |
datastore->data = details; |
|
|
525 |
|
| 526 |
/* initialize default T.38 parameters */ |
| 527 |
t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters); |
| 528 |
t38_parameters_ast_to_fax(&details->their_t38_parameters, &our_t38_parameters); |
| 529 |
|
| 422 |
ao2_ref(details, 1); |
530 |
ao2_ref(details, 1); |
| 423 |
ast_channel_lock(chan); |
531 |
ast_channel_lock(chan); |
| 424 |
ast_channel_datastore_add(chan, datastore); |
532 |
ast_channel_datastore_add(chan, datastore); |
|
Lines 511-516
Link Here
|
| 511 |
ast_build_string(&buf, &size, "MULTI_DOC"); |
619 |
ast_build_string(&buf, &size, "MULTI_DOC"); |
| 512 |
first = 0; |
620 |
first = 0; |
| 513 |
} |
621 |
} |
|
|
622 |
if (caps & AST_FAX_TECH_GATEWAY) { |
| 623 |
if (!first) { |
| 624 |
ast_build_string(&buf, &size, ","); |
| 625 |
} |
| 626 |
ast_build_string(&buf, &size, "T38_GATEWAY"); |
| 627 |
first = 0; |
| 628 |
} |
| 514 |
|
629 |
|
| 515 |
return out; |
630 |
return out; |
| 516 |
} |
631 |
} |
|
Lines 686-691
Link Here
|
| 686 |
} |
801 |
} |
| 687 |
} |
802 |
} |
| 688 |
|
803 |
|
|
|
804 |
/*! \brief Release a session token. |
| 805 |
* \param s a session returned from fax_session_reserve() |
| 806 |
* \param token a token generated from fax_session_reserve() |
| 807 |
* |
| 808 |
* This function releases the given token and marks the given session as no |
| 809 |
* longer reserved. It is safe to call on a session that is not actually |
| 810 |
* reserved and with a NULL token. This is so that sessions returned by |
| 811 |
* technologies that do not support reserved sessions don't require extra logic |
| 812 |
* to handle. |
| 813 |
* |
| 814 |
* \note This function DOES NOT release the given fax session, only the given |
| 815 |
* token. |
| 816 |
*/ |
| 689 |
static void fax_session_release(struct ast_fax_session *s, struct ast_fax_tech_token *token) |
817 |
static void fax_session_release(struct ast_fax_session *s, struct ast_fax_tech_token *token) |
| 690 |
{ |
818 |
{ |
| 691 |
if (token) { |
819 |
if (token) { |
|
Lines 732-737
Link Here
|
| 732 |
ast_free(s->chan_uniqueid); |
860 |
ast_free(s->chan_uniqueid); |
| 733 |
} |
861 |
} |
| 734 |
|
862 |
|
|
|
863 |
/*! \brief Reserve a fax session. |
| 864 |
* \param details the fax session details |
| 865 |
* \param token a pointer to a place to store a token to be passed to fax_session_new() later |
| 866 |
* |
| 867 |
* This function reserves a fax session for use later. If the selected fax |
| 868 |
* technology does not support reserving sessions a session will still be |
| 869 |
* returned but token will not be set. |
| 870 |
* |
| 871 |
* \note The reference returned by this function does not get consumed by |
| 872 |
* fax_session_new() and must always be dereferenced separately. |
| 873 |
* |
| 874 |
* \return NULL or an uninitialized and possibly reserved session |
| 875 |
*/ |
| 735 |
static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_details *details, struct ast_fax_tech_token **token) |
876 |
static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_details *details, struct ast_fax_tech_token **token) |
| 736 |
{ |
877 |
{ |
| 737 |
struct ast_fax_session *s; |
878 |
struct ast_fax_session *s; |
|
Lines 743-748
Link Here
|
| 743 |
} |
884 |
} |
| 744 |
|
885 |
|
| 745 |
s->state = AST_FAX_STATE_INACTIVE; |
886 |
s->state = AST_FAX_STATE_INACTIVE; |
|
|
887 |
s->details = details; |
| 888 |
ao2_ref(s->details, 1); |
| 746 |
|
889 |
|
| 747 |
/* locate a FAX technology module that can handle said requirements |
890 |
/* locate a FAX technology module that can handle said requirements |
| 748 |
* Note: the requirements have not yet been finalized as T.38 |
891 |
* Note: the requirements have not yet been finalized as T.38 |
|
Lines 781-787
Link Here
|
| 781 |
return s; |
924 |
return s; |
| 782 |
} |
925 |
} |
| 783 |
|
926 |
|
| 784 |
/*! \brief create a FAX session */ |
927 |
/*! \brief create a FAX session |
|
|
928 |
* |
| 929 |
* \param details details for the session |
| 930 |
* \param chan the channel the session will run on |
| 931 |
* \param reserved a reserved session to base this session on (can be NULL) |
| 932 |
* \param token the token for a reserved session (can be NULL) |
| 933 |
* |
| 934 |
* Create a new fax session based on the given details structure. |
| 935 |
* |
| 936 |
* \note The given token is always consumed (by tech->new_session() or by |
| 937 |
* fax_session_release() in the event of a failure). The given reference to a |
| 938 |
* reserved session is never consumed and must be dereferenced separately from |
| 939 |
* the reference returned by this function. |
| 940 |
* |
| 941 |
* \return NULL or a reference to a new fax session |
| 942 |
*/ |
| 785 |
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) |
943 |
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) |
| 786 |
{ |
944 |
{ |
| 787 |
struct ast_fax_session *s = NULL; |
945 |
struct ast_fax_session *s = NULL; |
|
Lines 792-797
Link Here
|
| 792 |
s = reserved; |
950 |
s = reserved; |
| 793 |
ao2_ref(reserved, +1); |
951 |
ao2_ref(reserved, +1); |
| 794 |
|
952 |
|
|
|
953 |
/* NOTE: we don't consume the reference to the reserved |
| 954 |
* session. The session returned from fax_session_new() is a |
| 955 |
* new reference and must be derefed in addition to the |
| 956 |
* reserved session. |
| 957 |
*/ |
| 958 |
|
| 795 |
if (s->state == AST_FAX_STATE_RESERVED) { |
959 |
if (s->state == AST_FAX_STATE_RESERVED) { |
| 796 |
ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1); |
960 |
ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1); |
| 797 |
s->state = AST_FAX_STATE_UNINITIALIZED; |
961 |
s->state = AST_FAX_STATE_UNINITIALIZED; |
|
Lines 834-841
Link Here
|
| 834 |
} |
998 |
} |
| 835 |
|
999 |
|
| 836 |
s->chan = chan; |
1000 |
s->chan = chan; |
| 837 |
s->details = details; |
1001 |
if (!s->details) { |
| 838 |
ao2_ref(s->details, 1); |
1002 |
s->details = details; |
|
|
1003 |
ao2_ref(s->details, 1); |
| 1004 |
} |
| 839 |
|
1005 |
|
| 840 |
details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1); |
1006 |
details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1); |
| 841 |
|
1007 |
|
|
Lines 935-943
Link Here
|
| 935 |
static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status) |
1101 |
static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status) |
| 936 |
{ |
1102 |
{ |
| 937 |
char *filenames = generate_filenames_string(details, "FileName: ", "\r\n"); |
1103 |
char *filenames = generate_filenames_string(details, "FileName: ", "\r\n"); |
| 938 |
if (!filenames) { |
|
|
| 939 |
return 1; |
| 940 |
} |
| 941 |
|
1104 |
|
| 942 |
ast_channel_lock(chan); |
1105 |
ast_channel_lock(chan); |
| 943 |
if (details->option.statusevents) { |
1106 |
if (details->option.statusevents) { |
|
Lines 945-969
Link Here
|
| 945 |
|
1108 |
|
| 946 |
get_manager_event_info(chan, &info); |
1109 |
get_manager_event_info(chan, &info); |
| 947 |
manager_event(EVENT_FLAG_CALL, |
1110 |
manager_event(EVENT_FLAG_CALL, |
| 948 |
(details->caps & AST_FAX_TECH_RECEIVE) ? "ReceiveFAXStatus" : "SendFAXStatus", |
1111 |
"FAXStatus", |
|
|
1112 |
"Operation: %s\r\n" |
| 949 |
"Status: %s\r\n" |
1113 |
"Status: %s\r\n" |
| 950 |
"Channel: %s\r\n" |
1114 |
"Channel: %s\r\n" |
| 951 |
"Context: %s\r\n" |
1115 |
"Context: %s\r\n" |
| 952 |
"Exten: %s\r\n" |
1116 |
"Exten: %s\r\n" |
| 953 |
"CallerID: %s\r\n" |
1117 |
"CallerID: %s\r\n" |
| 954 |
"LocalStationID: %s\r\n" |
1118 |
"LocalStationID: %s\r\n" |
| 955 |
"%s\r\n", |
1119 |
"%s%s", |
|
|
1120 |
(details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send", |
| 956 |
status, |
1121 |
status, |
| 957 |
chan->name, |
1122 |
chan->name, |
| 958 |
info.context, |
1123 |
info.context, |
| 959 |
info.exten, |
1124 |
info.exten, |
| 960 |
info.cid, |
1125 |
info.cid, |
| 961 |
details->localstationid, |
1126 |
details->localstationid, |
| 962 |
filenames); |
1127 |
S_OR(filenames, ""), |
|
|
1128 |
filenames ? "\r\n" : ""); |
| 963 |
} |
1129 |
} |
| 964 |
ast_channel_unlock(chan); |
1130 |
ast_channel_unlock(chan); |
| 965 |
ast_free(filenames); |
|
|
| 966 |
|
1131 |
|
|
|
1132 |
if (filenames) { |
| 1133 |
ast_free(filenames); |
| 1134 |
} |
| 1135 |
|
| 967 |
return 0; |
1136 |
return 0; |
| 968 |
} |
1137 |
} |
| 969 |
|
1138 |
|
|
Lines 1006-1042
Link Here
|
| 1006 |
GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \ |
1175 |
GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \ |
| 1007 |
} while (0) |
1176 |
} while (0) |
| 1008 |
|
1177 |
|
| 1009 |
static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src) |
|
|
| 1010 |
{ |
| 1011 |
dst->version = src->version; |
| 1012 |
dst->max_ifp = src->max_ifp; |
| 1013 |
dst->rate = src->rate; |
| 1014 |
dst->rate_management = src->rate_management; |
| 1015 |
dst->fill_bit_removal = src->fill_bit_removal; |
| 1016 |
dst->transcoding_mmr = src->transcoding_mmr; |
| 1017 |
dst->transcoding_jbig = src->transcoding_jbig; |
| 1018 |
} |
| 1019 |
|
| 1020 |
static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src) |
| 1021 |
{ |
| 1022 |
dst->version = src->version; |
| 1023 |
dst->max_ifp = src->max_ifp; |
| 1024 |
dst->rate = src->rate; |
| 1025 |
dst->rate_management = src->rate_management; |
| 1026 |
dst->fill_bit_removal = src->fill_bit_removal; |
| 1027 |
dst->transcoding_mmr = src->transcoding_mmr; |
| 1028 |
dst->transcoding_jbig = src->transcoding_jbig; |
| 1029 |
} |
| 1030 |
|
| 1031 |
static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_details *details) |
1178 |
static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_details *details) |
| 1032 |
{ |
1179 |
{ |
| 1033 |
switch (ast_channel_get_t38_state(chan)) { |
1180 |
switch (ast_channel_get_t38_state(chan)) { |
| 1034 |
case T38_STATE_UNKNOWN: |
1181 |
case T38_STATE_UNKNOWN: |
| 1035 |
details->caps |= AST_FAX_TECH_T38; |
1182 |
details->caps |= AST_FAX_TECH_T38; |
| 1036 |
break; |
1183 |
break; |
|
|
1184 |
case T38_STATE_REJECTED: |
| 1037 |
case T38_STATE_UNAVAILABLE: |
1185 |
case T38_STATE_UNAVAILABLE: |
| 1038 |
details->caps |= AST_FAX_TECH_AUDIO; |
1186 |
details->caps |= AST_FAX_TECH_AUDIO; |
| 1039 |
break; |
1187 |
break; |
|
|
1188 |
case T38_STATE_NEGOTIATED: |
| 1189 |
/* already in T.38 mode? This should not happen. */ |
| 1040 |
case T38_STATE_NEGOTIATING: { |
1190 |
case T38_STATE_NEGOTIATING: { |
| 1041 |
/* the other end already sent us a T.38 reinvite, so we need to prod the channel |
1191 |
/* the other end already sent us a T.38 reinvite, so we need to prod the channel |
| 1042 |
* driver into resending their parameters to us if it supports doing so... if |
1192 |
* driver into resending their parameters to us if it supports doing so... if |
|
Lines 1118-1130
Link Here
|
| 1118 |
return 0; |
1268 |
return 0; |
| 1119 |
} |
1269 |
} |
| 1120 |
|
1270 |
|
| 1121 |
static struct ast_control_t38_parameters our_t38_parameters = { |
|
|
| 1122 |
.version = 0, |
| 1123 |
.max_ifp = 400, |
| 1124 |
.rate = AST_T38_RATE_14400, |
| 1125 |
.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, |
| 1126 |
}; |
| 1127 |
|
| 1128 |
/*! \brief this is the generic FAX session handling function */ |
1271 |
/*! \brief this is the generic FAX session handling function */ |
| 1129 |
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) |
1272 |
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) |
| 1130 |
{ |
1273 |
{ |
|
Lines 1390-1397
Link Here
|
| 1390 |
struct ast_frame *frame = NULL; |
1533 |
struct ast_frame *frame = NULL; |
| 1391 |
struct ast_control_t38_parameters t38_parameters; |
1534 |
struct ast_control_t38_parameters t38_parameters; |
| 1392 |
|
1535 |
|
| 1393 |
t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters); |
|
|
| 1394 |
|
| 1395 |
/* don't send any audio if we've already received a T.38 reinvite */ |
1536 |
/* don't send any audio if we've already received a T.38 reinvite */ |
| 1396 |
if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) { |
1537 |
if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) { |
| 1397 |
/* generate 3 seconds of CED */ |
1538 |
/* generate 3 seconds of CED */ |
|
Lines 1551-1556
Link Here
|
| 1551 |
); |
1692 |
); |
| 1552 |
struct ast_flags opts = { 0, }; |
1693 |
struct ast_flags opts = { 0, }; |
| 1553 |
struct manager_event_info info; |
1694 |
struct manager_event_info info; |
|
|
1695 |
enum ast_t38_state t38state; |
| 1554 |
|
1696 |
|
| 1555 |
/* initialize output channel variables */ |
1697 |
/* initialize output channel variables */ |
| 1556 |
pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED"); |
1698 |
pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED"); |
|
Lines 1579-1584
Link Here
|
| 1579 |
ast_string_field_set(details, error, "INIT_ERROR"); |
1721 |
ast_string_field_set(details, error, "INIT_ERROR"); |
| 1580 |
set_channel_variables(chan, details); |
1722 |
set_channel_variables(chan, details); |
| 1581 |
|
1723 |
|
|
|
1724 |
if (details->caps & AST_FAX_TECH_GATEWAY) { |
| 1725 |
ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway"); |
| 1726 |
set_channel_variables(chan, details); |
| 1727 |
ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n"); |
| 1728 |
ao2_ref(details, -1); |
| 1729 |
return -1; |
| 1730 |
} |
| 1731 |
|
| 1582 |
if (details->maxrate < details->minrate) { |
1732 |
if (details->maxrate < details->minrate) { |
| 1583 |
ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); |
1733 |
ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); |
| 1584 |
ast_string_field_set(details, error, "INVALID_ARGUMENTS"); |
1734 |
ast_string_field_set(details, error, "INVALID_ARGUMENTS"); |
|
Lines 1683-1689
Link Here
|
| 1683 |
details->option.statusevents = AST_FAX_OPTFLAG_TRUE; |
1833 |
details->option.statusevents = AST_FAX_OPTFLAG_TRUE; |
| 1684 |
} |
1834 |
} |
| 1685 |
|
1835 |
|
| 1686 |
if ((ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE) || |
1836 |
t38state = ast_channel_get_t38_state(chan); |
|
|
1837 |
if ((t38state == T38_STATE_UNAVAILABLE) || (t38state == T38_STATE_REJECTED) || |
| 1687 |
ast_test_flag(&opts, OPT_ALLOWAUDIO)) { |
1838 |
ast_test_flag(&opts, OPT_ALLOWAUDIO)) { |
| 1688 |
details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; |
1839 |
details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; |
| 1689 |
} |
1840 |
} |
|
Lines 1789-1796
Link Here
|
| 1789 |
struct ast_frame *frame = NULL; |
1940 |
struct ast_frame *frame = NULL; |
| 1790 |
struct ast_control_t38_parameters t38_parameters; |
1941 |
struct ast_control_t38_parameters t38_parameters; |
| 1791 |
|
1942 |
|
| 1792 |
t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters); |
|
|
| 1793 |
|
| 1794 |
/* send CNG tone while listening for the receiver to initiate a switch |
1943 |
/* send CNG tone while listening for the receiver to initiate a switch |
| 1795 |
* to T.38 mode; if they do, stop sending the CNG tone and proceed with |
1944 |
* to T.38 mode; if they do, stop sending the CNG tone and proceed with |
| 1796 |
* the switch. |
1945 |
* the switch. |
|
Lines 2023-2028
Link Here
|
| 2023 |
); |
2172 |
); |
| 2024 |
struct ast_flags opts = { 0, }; |
2173 |
struct ast_flags opts = { 0, }; |
| 2025 |
struct manager_event_info info; |
2174 |
struct manager_event_info info; |
|
|
2175 |
enum ast_t38_state t38state; |
| 2026 |
|
2176 |
|
| 2027 |
/* initialize output channel variables */ |
2177 |
/* initialize output channel variables */ |
| 2028 |
pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED"); |
2178 |
pbx_builtin_setvar_helper(chan, "FAXSTATUS", "FAILED"); |
|
Lines 2051-2056
Link Here
|
| 2051 |
ast_string_field_set(details, error, "INIT_ERROR"); |
2201 |
ast_string_field_set(details, error, "INIT_ERROR"); |
| 2052 |
set_channel_variables(chan, details); |
2202 |
set_channel_variables(chan, details); |
| 2053 |
|
2203 |
|
|
|
2204 |
if (details->caps & AST_FAX_TECH_GATEWAY) { |
| 2205 |
ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway"); |
| 2206 |
set_channel_variables(chan, details); |
| 2207 |
ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n"); |
| 2208 |
ao2_ref(details, -1); |
| 2209 |
return -1; |
| 2210 |
} |
| 2211 |
|
| 2054 |
if (details->maxrate < details->minrate) { |
2212 |
if (details->maxrate < details->minrate) { |
| 2055 |
ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); |
2213 |
ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); |
| 2056 |
ast_string_field_set(details, error, "INVALID_ARGUMENTS"); |
2214 |
ast_string_field_set(details, error, "INVALID_ARGUMENTS"); |
|
Lines 2175-2181
Link Here
|
| 2175 |
details->option.statusevents = AST_FAX_OPTFLAG_TRUE; |
2333 |
details->option.statusevents = AST_FAX_OPTFLAG_TRUE; |
| 2176 |
} |
2334 |
} |
| 2177 |
|
2335 |
|
| 2178 |
if ((ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE) || |
2336 |
t38state = ast_channel_get_t38_state(chan); |
|
|
2337 |
if ((t38state == T38_STATE_UNAVAILABLE) || (t38state == T38_STATE_REJECTED) || |
| 2179 |
ast_test_flag(&opts, OPT_ALLOWAUDIO)) { |
2338 |
ast_test_flag(&opts, OPT_ALLOWAUDIO)) { |
| 2180 |
details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; |
2339 |
details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; |
| 2181 |
} |
2340 |
} |
|
Lines 2287-2292
Link Here
|
| 2287 |
return (!channel_alive) ? -1 : 0; |
2446 |
return (!channel_alive) ? -1 : 0; |
| 2288 |
} |
2447 |
} |
| 2289 |
|
2448 |
|
|
|
2449 |
/*! \brief destroy a FAX gateway session structure */ |
| 2450 |
static void destroy_gateway(void *data) |
| 2451 |
{ |
| 2452 |
struct fax_gateway *gateway = data; |
| 2453 |
|
| 2454 |
if (gateway->chan_dsp) { |
| 2455 |
ast_dsp_free(gateway->chan_dsp); |
| 2456 |
gateway->chan_dsp = NULL; |
| 2457 |
} |
| 2458 |
|
| 2459 |
if (gateway->peer_dsp) { |
| 2460 |
ast_dsp_free(gateway->peer_dsp); |
| 2461 |
gateway->peer_dsp = NULL; |
| 2462 |
} |
| 2463 |
|
| 2464 |
if (gateway->s) { |
| 2465 |
fax_session_release(gateway->s, gateway->token); |
| 2466 |
gateway->token = NULL; |
| 2467 |
gateway->s->details->caps |= ~AST_FAX_TECH_GATEWAY; |
| 2468 |
|
| 2469 |
ao2_lock(faxregistry.container); |
| 2470 |
ao2_unlink(faxregistry.container, gateway->s); |
| 2471 |
ao2_unlock(faxregistry.container); |
| 2472 |
|
| 2473 |
ao2_ref(gateway->s, -1); |
| 2474 |
gateway->s = NULL; |
| 2475 |
} |
| 2476 |
} |
| 2477 |
|
| 2478 |
/*! \brief Create a new fax gateway object. |
| 2479 |
* \param details the fax session details |
| 2480 |
* \return NULL or a fax gateway object |
| 2481 |
*/ |
| 2482 |
static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *details) |
| 2483 |
{ |
| 2484 |
struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway); |
| 2485 |
if (!gateway) { |
| 2486 |
return NULL; |
| 2487 |
} |
| 2488 |
|
| 2489 |
gateway->chan_dsp = ast_dsp_new(); |
| 2490 |
if (!gateway->chan_dsp) { |
| 2491 |
ao2_ref(gateway, -1); |
| 2492 |
return NULL; |
| 2493 |
} |
| 2494 |
|
| 2495 |
gateway->peer_dsp = ast_dsp_new(); |
| 2496 |
if (!gateway->peer_dsp) { |
| 2497 |
ao2_ref(gateway, -1); |
| 2498 |
return NULL; |
| 2499 |
} |
| 2500 |
|
| 2501 |
gateway->framehook = -1; |
| 2502 |
|
| 2503 |
ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT); |
| 2504 |
ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21 | DSP_FAXMODE_DETECT_CED); |
| 2505 |
|
| 2506 |
ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT); |
| 2507 |
ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21 | DSP_FAXMODE_DETECT_CED); |
| 2508 |
|
| 2509 |
details->caps = AST_FAX_TECH_GATEWAY; |
| 2510 |
if (details->gateway_timeout && !(gateway->s = fax_session_reserve(details, &gateway->token))) { |
| 2511 |
details->caps |= ~AST_FAX_TECH_GATEWAY; |
| 2512 |
ast_log(LOG_ERROR, "Can't reserve a FAX session, gateway attempt failed.\n"); |
| 2513 |
ao2_ref(gateway, -1); |
| 2514 |
return NULL; |
| 2515 |
} |
| 2516 |
|
| 2517 |
return gateway; |
| 2518 |
} |
| 2519 |
|
| 2520 |
/*! \brief Create a fax session and start T.30<->T.38 gateway mode |
| 2521 |
* \param gateway a fax gateway object |
| 2522 |
* \param details fax session details |
| 2523 |
* \param chan active channel |
| 2524 |
* \return 0 on error 1 on success*/ |
| 2525 |
static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan) |
| 2526 |
{ |
| 2527 |
struct ast_fax_session *s; |
| 2528 |
|
| 2529 |
/* create the FAX session */ |
| 2530 |
if (!(s = fax_session_new(details, chan, gateway->s, gateway->token))) { |
| 2531 |
gateway->token = NULL; |
| 2532 |
ast_string_field_set(details, result, "FAILED"); |
| 2533 |
ast_string_field_set(details, resultstr, "error starting gateway session"); |
| 2534 |
ast_string_field_set(details, error, "INIT_ERROR"); |
| 2535 |
set_channel_variables(chan, details); |
| 2536 |
report_fax_status(chan, details, "No Available Resource"); |
| 2537 |
ast_log(LOG_ERROR, "Can't create a FAX session, gateway attempt failed.\n"); |
| 2538 |
return -1; |
| 2539 |
} |
| 2540 |
/* release the reference for the reserved session and replace it with |
| 2541 |
* the real session */ |
| 2542 |
ao2_ref(gateway->s, -1); |
| 2543 |
gateway->s = s; |
| 2544 |
gateway->token = NULL; |
| 2545 |
|
| 2546 |
if (gateway->s->tech->start_session(gateway->s) < 0) { |
| 2547 |
ast_string_field_set(details, result, "FAILED"); |
| 2548 |
ast_string_field_set(details, resultstr, "error starting gateway session"); |
| 2549 |
ast_string_field_set(details, error, "INIT_ERROR"); |
| 2550 |
set_channel_variables(chan, details); |
| 2551 |
return -1; |
| 2552 |
} |
| 2553 |
|
| 2554 |
gateway->timeout_start.tv_sec = 0; |
| 2555 |
gateway->timeout_start.tv_usec = 0; |
| 2556 |
|
| 2557 |
report_fax_status(chan, details, "FAX Transmission In Progress"); |
| 2558 |
|
| 2559 |
return 0; |
| 2560 |
} |
| 2561 |
|
| 2562 |
static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_frame *f) |
| 2563 |
{ |
| 2564 |
struct ast_frame *fp; |
| 2565 |
struct ast_control_t38_parameters t38_parameters = { |
| 2566 |
.request_response = AST_T38_REQUEST_NEGOTIATE, |
| 2567 |
}; |
| 2568 |
struct ast_frame control_frame = { |
| 2569 |
.src = "res_fax", |
| 2570 |
.frametype = AST_FRAME_CONTROL, |
| 2571 |
.datalen = sizeof(t38_parameters), |
| 2572 |
.subclass.integer = AST_CONTROL_T38_PARAMETERS, |
| 2573 |
.data.ptr = &t38_parameters, |
| 2574 |
}; |
| 2575 |
|
| 2576 |
struct ast_fax_session_details *details = find_details(chan); |
| 2577 |
|
| 2578 |
if (!details) { |
| 2579 |
ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name); |
| 2580 |
ast_framehook_detach(chan, gateway->framehook); |
| 2581 |
return f; |
| 2582 |
} |
| 2583 |
|
| 2584 |
t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters); |
| 2585 |
ao2_ref(details, -1); |
| 2586 |
|
| 2587 |
if (!(fp = ast_frisolate(&control_frame))) { |
| 2588 |
ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name); |
| 2589 |
return f; |
| 2590 |
} |
| 2591 |
|
| 2592 |
gateway->t38_state = T38_STATE_NEGOTIATING; |
| 2593 |
gateway->timeout_start = ast_tvnow(); |
| 2594 |
details->gateway_timeout = FAX_GATEWAY_TIMEOUT; |
| 2595 |
|
| 2596 |
ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name); |
| 2597 |
return fp; |
| 2598 |
} |
| 2599 |
|
| 2600 |
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) |
| 2601 |
{ |
| 2602 |
struct ast_frame *dfr = ast_frdup(f); |
| 2603 |
struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp; |
| 2604 |
struct ast_channel *other = (active == chan) ? peer : chan; |
| 2605 |
|
| 2606 |
if (gateway->detected_v21) { |
| 2607 |
return f; |
| 2608 |
} |
| 2609 |
|
| 2610 |
if (!dfr) { |
| 2611 |
return f; |
| 2612 |
} |
| 2613 |
|
| 2614 |
if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) { |
| 2615 |
return f; |
| 2616 |
} |
| 2617 |
|
| 2618 |
if (dfr->frametype == AST_FRAME_DTMF && ((dfr->subclass.integer == 'e') || (dfr->subclass.integer == 'g'))) { |
| 2619 |
gateway->detected_v21 = 1; |
| 2620 |
if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) { |
| 2621 |
ast_debug(1, "detected v21 preamble from %s\n", active->name); |
| 2622 |
return fax_gateway_request_t38(gateway, chan, f); |
| 2623 |
} else { |
| 2624 |
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); |
| 2625 |
} |
| 2626 |
} |
| 2627 |
|
| 2628 |
ast_frfree(dfr); |
| 2629 |
return f; |
| 2630 |
} |
| 2631 |
|
| 2632 |
static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel *active, struct ast_control_t38_parameters *control_params) |
| 2633 |
{ |
| 2634 |
if (active == chan) { |
| 2635 |
return ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params)); |
| 2636 |
} else { |
| 2637 |
return ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params)); |
| 2638 |
} |
| 2639 |
} |
| 2640 |
|
| 2641 |
/*! \brief T38 Gateway Negotiate t38 parameters |
| 2642 |
* \param gateway gateway object |
| 2643 |
* \param chan channel running the gateway |
| 2644 |
* \param peer channel im bridged too |
| 2645 |
* \param active channel the frame originated on |
| 2646 |
* \param f the control frame to process |
| 2647 |
* \return processed control frame or null frame |
| 2648 |
*/ |
| 2649 |
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) |
| 2650 |
{ |
| 2651 |
struct ast_control_t38_parameters *control_params = f->data.ptr; |
| 2652 |
struct ast_channel *other = (active == chan) ? peer : chan; |
| 2653 |
struct ast_fax_session_details *details; |
| 2654 |
|
| 2655 |
if (f->datalen != sizeof(struct ast_control_t38_parameters)) { |
| 2656 |
/* invalaid AST_CONTROL_T38_PARAMETERS frame, we can't |
| 2657 |
* do anything with it, pass it on */ |
| 2658 |
return f; |
| 2659 |
} |
| 2660 |
|
| 2661 |
/* ignore frames from ourselves */ |
| 2662 |
if ((gateway->t38_state == T38_STATE_NEGOTIATED && control_params->request_response == AST_T38_NEGOTIATED) |
| 2663 |
|| (gateway->t38_state == T38_STATE_REJECTED && control_params->request_response == AST_T38_REFUSED) |
| 2664 |
|| (gateway->t38_state == T38_STATE_NEGOTIATING && control_params->request_response == AST_T38_REQUEST_TERMINATE)) { |
| 2665 |
|
| 2666 |
return f; |
| 2667 |
} |
| 2668 |
|
| 2669 |
if (!(details = find_details(chan))) { |
| 2670 |
ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name); |
| 2671 |
ast_framehook_detach(chan, gateway->framehook); |
| 2672 |
return f; |
| 2673 |
} |
| 2674 |
|
| 2675 |
if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) { |
| 2676 |
enum ast_t38_state state = ast_channel_get_t38_state(other); |
| 2677 |
|
| 2678 |
if (state == T38_STATE_UNKNOWN) { |
| 2679 |
/* we detected a request to negotiate T.38 and the |
| 2680 |
* other channel appears to support T.38, we'll pass |
| 2681 |
* the request through and only step in if the other |
| 2682 |
* channel rejects the request */ |
| 2683 |
ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", active->name, other->name); |
| 2684 |
t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); |
| 2685 |
gateway->t38_state = T38_STATE_UNKNOWN; |
| 2686 |
gateway->timeout_start = ast_tvnow(); |
| 2687 |
details->gateway_timeout = FAX_GATEWAY_TIMEOUT; |
| 2688 |
ao2_ref(details, -1); |
| 2689 |
return f; |
| 2690 |
} else if (state == T38_STATE_UNAVAILABLE || state == T38_STATE_REJECTED) { |
| 2691 |
/* the other channel does not support T.38, we need to |
| 2692 |
* step in here */ |
| 2693 |
ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", active->name, other->name); |
| 2694 |
ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); |
| 2695 |
|
| 2696 |
t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); |
| 2697 |
t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters); |
| 2698 |
|
| 2699 |
if (fax_gateway_start(gateway, details, chan)) { |
| 2700 |
ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); |
| 2701 |
gateway->t38_state = T38_STATE_REJECTED; |
| 2702 |
control_params->request_response = AST_T38_REFUSED; |
| 2703 |
|
| 2704 |
ast_framehook_detach(chan, details->gateway_id); |
| 2705 |
details->gateway_id = -1; |
| 2706 |
} else { |
| 2707 |
gateway->t38_state = T38_STATE_NEGOTIATED; |
| 2708 |
control_params->request_response = AST_T38_NEGOTIATED; |
| 2709 |
report_fax_status(chan, details, "T.38 Negotiated"); |
| 2710 |
} |
| 2711 |
|
| 2712 |
fax_gateway_indicate_t38(chan, active, control_params); |
| 2713 |
|
| 2714 |
ao2_ref(details, -1); |
| 2715 |
return &ast_null_frame; |
| 2716 |
} else if (gateway->t38_state == T38_STATE_NEGOTIATING) { |
| 2717 |
/* we got a request to negotiate T.38 after we already |
| 2718 |
* sent one to the other party based on v21 preamble |
| 2719 |
* detection. We'll just pretend we passed this request |
| 2720 |
* through in the first place. */ |
| 2721 |
|
| 2722 |
t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); |
| 2723 |
gateway->t38_state = T38_STATE_UNKNOWN; |
| 2724 |
gateway->timeout_start = ast_tvnow(); |
| 2725 |
details->gateway_timeout = FAX_GATEWAY_TIMEOUT; |
| 2726 |
|
| 2727 |
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); |
| 2728 |
ao2_ref(details, -1); |
| 2729 |
return &ast_null_frame; |
| 2730 |
} else if (gateway->t38_state == T38_STATE_NEGOTIATED) { |
| 2731 |
/* we got a request to negotiate T.38 after we already |
| 2732 |
* sent one to the other party based on v21 preamble |
| 2733 |
* detection and received a response. We need to |
| 2734 |
* respond to this and shut down the gateway. */ |
| 2735 |
|
| 2736 |
t38_parameters_fax_to_ast(control_params, &details->their_t38_parameters); |
| 2737 |
ast_framehook_detach(chan, details->gateway_id); |
| 2738 |
details->gateway_id = -1; |
| 2739 |
|
| 2740 |
control_params->request_response = AST_T38_NEGOTIATED; |
| 2741 |
|
| 2742 |
fax_gateway_indicate_t38(chan, active, control_params); |
| 2743 |
|
| 2744 |
ast_string_field_set(details, result, "SUCCESS"); |
| 2745 |
ast_string_field_set(details, resultstr, "no gateway necessary"); |
| 2746 |
ast_string_field_set(details, error, "NATIVE_T38"); |
| 2747 |
set_channel_variables(chan, details); |
| 2748 |
|
| 2749 |
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); |
| 2750 |
ao2_ref(details, -1); |
| 2751 |
return &ast_null_frame; |
| 2752 |
} else { |
| 2753 |
ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", active->name, other->name); |
| 2754 |
ao2_ref(details, -1); |
| 2755 |
return f; |
| 2756 |
} |
| 2757 |
} else if (gateway->t38_state == T38_STATE_NEGOTIATING |
| 2758 |
&& control_params->request_response == AST_T38_REFUSED) { |
| 2759 |
|
| 2760 |
ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", active->name); |
| 2761 |
|
| 2762 |
/* our request to negotiate T.38 was refused, if the other |
| 2763 |
* channel supports T.38, they might still reinvite and save |
| 2764 |
* the day. Otherwise disable the gateway. */ |
| 2765 |
if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) { |
| 2766 |
gateway->t38_state = T38_STATE_UNAVAILABLE; |
| 2767 |
} else { |
| 2768 |
ast_framehook_detach(chan, details->gateway_id); |
| 2769 |
details->gateway_id = -1; |
| 2770 |
|
| 2771 |
ast_string_field_set(details, result, "FAILED"); |
| 2772 |
ast_string_field_set(details, resultstr, "unable to negotiate T.38"); |
| 2773 |
ast_string_field_set(details, error, "T38_NEG_ERROR"); |
| 2774 |
set_channel_variables(chan, details); |
| 2775 |
} |
| 2776 |
|
| 2777 |
ao2_ref(details, -1); |
| 2778 |
return &ast_null_frame; |
| 2779 |
} else if (gateway->t38_state == T38_STATE_NEGOTIATING |
| 2780 |
&& control_params->request_response == AST_T38_NEGOTIATED) { |
| 2781 |
|
| 2782 |
ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); |
| 2783 |
|
| 2784 |
t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params); |
| 2785 |
|
| 2786 |
if (fax_gateway_start(gateway, details, chan)) { |
| 2787 |
ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); |
| 2788 |
gateway->t38_state = T38_STATE_NEGOTIATING; |
| 2789 |
control_params->request_response = AST_T38_REQUEST_TERMINATE; |
| 2790 |
|
| 2791 |
fax_gateway_indicate_t38(chan, active, control_params); |
| 2792 |
} else { |
| 2793 |
gateway->t38_state = T38_STATE_NEGOTIATED; |
| 2794 |
report_fax_status(chan, details, "T.38 Negotiated"); |
| 2795 |
} |
| 2796 |
|
| 2797 |
ao2_ref(details, -1); |
| 2798 |
return &ast_null_frame; |
| 2799 |
} else if (control_params->request_response == AST_T38_REFUSED) { |
| 2800 |
/* the other channel refused the request to negotiate T.38, |
| 2801 |
* we'll step in here and pretend the request was accepted */ |
| 2802 |
|
| 2803 |
ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", other->name, active->name); |
| 2804 |
ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", other->name, active->name); |
| 2805 |
|
| 2806 |
t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters); |
| 2807 |
|
| 2808 |
if (fax_gateway_start(gateway, details, chan)) { |
| 2809 |
ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name); |
| 2810 |
gateway->t38_state = T38_STATE_REJECTED; |
| 2811 |
control_params->request_response = AST_T38_REFUSED; |
| 2812 |
|
| 2813 |
ast_framehook_detach(chan, details->gateway_id); |
| 2814 |
details->gateway_id = -1; |
| 2815 |
} else { |
| 2816 |
gateway->t38_state = T38_STATE_NEGOTIATED; |
| 2817 |
control_params->request_response = AST_T38_NEGOTIATED; |
| 2818 |
} |
| 2819 |
|
| 2820 |
ao2_ref(details, -1); |
| 2821 |
return f; |
| 2822 |
} else if (control_params->request_response == AST_T38_REQUEST_TERMINATE) { |
| 2823 |
/* the channel wishes to end our short relationship, we shall |
| 2824 |
* oblige */ |
| 2825 |
|
| 2826 |
ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", active->name); |
| 2827 |
|
| 2828 |
ast_framehook_detach(chan, details->gateway_id); |
| 2829 |
details->gateway_id = -1; |
| 2830 |
|
| 2831 |
gateway->t38_state = T38_STATE_REJECTED; |
| 2832 |
control_params->request_response = AST_T38_TERMINATED; |
| 2833 |
|
| 2834 |
fax_gateway_indicate_t38(chan, active, control_params); |
| 2835 |
|
| 2836 |
ao2_ref(details, -1); |
| 2837 |
return &ast_null_frame; |
| 2838 |
} else if (control_params->request_response == AST_T38_NEGOTIATED) { |
| 2839 |
ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", active->name, other->name); |
| 2840 |
|
| 2841 |
ast_framehook_detach(chan, details->gateway_id); |
| 2842 |
details->gateway_id = -1; |
| 2843 |
|
| 2844 |
ast_string_field_set(details, result, "SUCCESS"); |
| 2845 |
ast_string_field_set(details, resultstr, "no gateway necessary"); |
| 2846 |
ast_string_field_set(details, error, "NATIVE_T38"); |
| 2847 |
set_channel_variables(chan, details); |
| 2848 |
|
| 2849 |
ao2_ref(details, -1); |
| 2850 |
return f; |
| 2851 |
} else if (control_params->request_response == AST_T38_TERMINATED) { |
| 2852 |
ast_debug(1, "T.38 disabled on channel %s\n", active->name); |
| 2853 |
|
| 2854 |
ast_framehook_detach(chan, details->gateway_id); |
| 2855 |
details->gateway_id = -1; |
| 2856 |
|
| 2857 |
ao2_ref(details, -1); |
| 2858 |
return &ast_null_frame; |
| 2859 |
} |
| 2860 |
|
| 2861 |
ao2_ref(details, -1); |
| 2862 |
return f; |
| 2863 |
} |
| 2864 |
|
| 2865 |
/*! \brief Destroy the gateway data structure when the framehook is detached |
| 2866 |
* \param data framehook data (gateway data)*/ |
| 2867 |
static void fax_gateway_framehook_destroy(void *data) { |
| 2868 |
struct fax_gateway *gateway = data; |
| 2869 |
|
| 2870 |
if (gateway->s) { |
| 2871 |
switch (gateway->s->state) { |
| 2872 |
case AST_FAX_STATE_INITIALIZED: |
| 2873 |
case AST_FAX_STATE_OPEN: |
| 2874 |
case AST_FAX_STATE_ACTIVE: |
| 2875 |
case AST_FAX_STATE_COMPLETE: |
| 2876 |
if (gateway->s->tech->cancel_session) { |
| 2877 |
gateway->s->tech->cancel_session(gateway->s); |
| 2878 |
} |
| 2879 |
/* fall through */ |
| 2880 |
default: |
| 2881 |
break; |
| 2882 |
} |
| 2883 |
} |
| 2884 |
|
| 2885 |
ao2_ref(gateway, -1); |
| 2886 |
} |
| 2887 |
|
| 2888 |
/*! \brief T.30<->T.38 gateway framehook. |
| 2889 |
* |
| 2890 |
* Intercept packets on bridged channels and determine if a T.38 gateway is |
| 2891 |
* required. If a gateway is required, start a gateway and handle T.38 |
| 2892 |
* negotiation if necessary. |
| 2893 |
* |
| 2894 |
* \param chan channel running the gateway |
| 2895 |
* \param f frame to handle may be NULL |
| 2896 |
* \param event framehook event |
| 2897 |
* \param data framehook data (struct fax_gateway *) |
| 2898 |
* |
| 2899 |
* \return processed frame or NULL when f is NULL or a null frame |
| 2900 |
*/ |
| 2901 |
static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) { |
| 2902 |
struct fax_gateway *gateway = data; |
| 2903 |
struct ast_channel *peer, *active; |
| 2904 |
struct ast_fax_session_details *details; |
| 2905 |
|
| 2906 |
if (gateway->s) { |
| 2907 |
details = gateway->s->details; |
| 2908 |
ao2_ref(details, 1); |
| 2909 |
} else { |
| 2910 |
if (!(details = find_details(chan))) { |
| 2911 |
ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name); |
| 2912 |
ast_framehook_detach(chan, gateway->framehook); |
| 2913 |
details->gateway_id = -1; |
| 2914 |
return f; |
| 2915 |
} |
| 2916 |
} |
| 2917 |
|
| 2918 |
/* restore audio formats when we are detached */ |
| 2919 |
if (event == AST_FRAMEHOOK_EVENT_DETACHED) { |
| 2920 |
set_channel_variables(chan, details); |
| 2921 |
|
| 2922 |
if (gateway->bridged) { |
| 2923 |
ast_set_read_format(chan, gateway->chan_read_format); |
| 2924 |
ast_set_read_format(chan, gateway->chan_write_format); |
| 2925 |
|
| 2926 |
if ((peer = ast_bridged_channel(chan))) { |
| 2927 |
ast_set_read_format(peer, gateway->peer_read_format); |
| 2928 |
ast_set_read_format(peer, gateway->peer_write_format); |
| 2929 |
ast_channel_make_compatible(chan, peer); |
| 2930 |
} |
| 2931 |
} |
| 2932 |
|
| 2933 |
ao2_ref(details, -1); |
| 2934 |
return NULL; |
| 2935 |
} |
| 2936 |
|
| 2937 |
if (!f || (event == AST_FRAMEHOOK_EVENT_ATTACHED)) { |
| 2938 |
ao2_ref(details, -1); |
| 2939 |
return NULL; |
| 2940 |
}; |
| 2941 |
|
| 2942 |
/* this frame was generated by the fax gateway, pass it on */ |
| 2943 |
if (ast_test_flag(f, AST_FAX_FRFLAG_GATEWAY)) { |
| 2944 |
ao2_ref(details, -1); |
| 2945 |
return f; |
| 2946 |
} |
| 2947 |
|
| 2948 |
if (!(peer = ast_bridged_channel(chan))) { |
| 2949 |
/* not bridged, don't do anything */ |
| 2950 |
ao2_ref(details, -1); |
| 2951 |
return f; |
| 2952 |
} |
| 2953 |
|
| 2954 |
if (!gateway->bridged && peer) { |
| 2955 |
/* don't start a gateway if neither channel can handle T.38 */ |
| 2956 |
if (ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE && ast_channel_get_t38_state(peer) == T38_STATE_UNAVAILABLE) { |
| 2957 |
ast_debug(1, "not starting gateway for %s and %s; neither channel supports T.38\n", chan->name, peer->name); |
| 2958 |
ast_framehook_detach(chan, gateway->framehook); |
| 2959 |
details->gateway_id = -1; |
| 2960 |
|
| 2961 |
ast_string_field_set(details, result, "FAILED"); |
| 2962 |
ast_string_field_set(details, resultstr, "neither channel supports T.38"); |
| 2963 |
ast_string_field_set(details, error, "T38_NEG_ERROR"); |
| 2964 |
set_channel_variables(chan, details); |
| 2965 |
ao2_ref(details, -1); |
| 2966 |
return f; |
| 2967 |
} |
| 2968 |
|
| 2969 |
if (details->gateway_timeout) { |
| 2970 |
gateway->timeout_start = ast_tvnow(); |
| 2971 |
} |
| 2972 |
|
| 2973 |
/* we are bridged, change r/w formats to SLIN for v21 preamble |
| 2974 |
* detection and T.30 */ |
| 2975 |
gateway->chan_read_format = chan->readformat; |
| 2976 |
gateway->chan_write_format = chan->readformat; |
| 2977 |
|
| 2978 |
gateway->peer_read_format = peer->readformat; |
| 2979 |
gateway->peer_write_format = peer->readformat; |
| 2980 |
|
| 2981 |
ast_set_read_format(chan, AST_FORMAT_SLINEAR); |
| 2982 |
ast_set_write_format(chan, AST_FORMAT_SLINEAR); |
| 2983 |
|
| 2984 |
ast_set_read_format(peer, AST_FORMAT_SLINEAR); |
| 2985 |
ast_set_write_format(peer, AST_FORMAT_SLINEAR); |
| 2986 |
|
| 2987 |
ast_channel_make_compatible(chan, peer); |
| 2988 |
gateway->bridged = 1; |
| 2989 |
} |
| 2990 |
|
| 2991 |
if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) { |
| 2992 |
if (ast_tvdiff_ms(ast_tvnow(), gateway->timeout_start) > details->gateway_timeout) { |
| 2993 |
ast_debug(1, "no fax activity between %s and %s after %d ms, disabling gateway\n", chan->name, peer->name, details->gateway_timeout); |
| 2994 |
ast_framehook_detach(chan, gateway->framehook); |
| 2995 |
details->gateway_id = -1; |
| 2996 |
|
| 2997 |
ast_string_field_set(details, result, "FAILED"); |
| 2998 |
ast_string_field_build(details, resultstr, "no fax activity after %d ms", details->gateway_timeout); |
| 2999 |
ast_string_field_set(details, error, "TIMEOUT"); |
| 3000 |
set_channel_variables(chan, details); |
| 3001 |
ao2_ref(details, -1); |
| 3002 |
return f; |
| 3003 |
} |
| 3004 |
} |
| 3005 |
|
| 3006 |
/* only handle VOICE, MODEM, and CONTROL frames*/ |
| 3007 |
switch (f->frametype) { |
| 3008 |
case AST_FRAME_VOICE: |
| 3009 |
switch (f->subclass.codec) { |
| 3010 |
case AST_FORMAT_SLINEAR: |
| 3011 |
case AST_FORMAT_ALAW: |
| 3012 |
case AST_FORMAT_ULAW: |
| 3013 |
break; |
| 3014 |
default: |
| 3015 |
ao2_ref(details, -1); |
| 3016 |
return f; |
| 3017 |
} |
| 3018 |
break; |
| 3019 |
case AST_FRAME_MODEM: |
| 3020 |
if (f->subclass.integer == AST_MODEM_T38) { |
| 3021 |
break; |
| 3022 |
} |
| 3023 |
ao2_ref(details, -1); |
| 3024 |
return f; |
| 3025 |
case AST_FRAME_CONTROL: |
| 3026 |
if (f->subclass.integer == AST_CONTROL_T38_PARAMETERS) { |
| 3027 |
break; |
| 3028 |
} |
| 3029 |
ao2_ref(details, -1); |
| 3030 |
return f; |
| 3031 |
default: |
| 3032 |
ao2_ref(details, -1); |
| 3033 |
return f; |
| 3034 |
} |
| 3035 |
|
| 3036 |
/* detect the active channel */ |
| 3037 |
switch (event) { |
| 3038 |
case AST_FRAMEHOOK_EVENT_WRITE: |
| 3039 |
active = peer; |
| 3040 |
break; |
| 3041 |
case AST_FRAMEHOOK_EVENT_READ: |
| 3042 |
active = chan; |
| 3043 |
break; |
| 3044 |
default: |
| 3045 |
ast_log(LOG_WARNING, "unhandled framehook event %i\n", event); |
| 3046 |
ao2_ref(details, -1); |
| 3047 |
return f; |
| 3048 |
} |
| 3049 |
|
| 3050 |
/* handle control frames */ |
| 3051 |
if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) { |
| 3052 |
ao2_ref(details, -1); |
| 3053 |
return fax_gateway_detect_t38(gateway, chan, peer, active, f); |
| 3054 |
} |
| 3055 |
|
| 3056 |
if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) { |
| 3057 |
/* not in gateway mode and have not detected v21 yet, listen |
| 3058 |
* for v21 */ |
| 3059 |
ao2_ref(details, -1); |
| 3060 |
return fax_gateway_detect_v21(gateway, chan, peer, active, f); |
| 3061 |
} |
| 3062 |
|
| 3063 |
/* in gateway mode, gateway some packets */ |
| 3064 |
if (gateway->t38_state == T38_STATE_NEGOTIATED) { |
| 3065 |
/* framehooks are called in __ast_read() before frame format |
| 3066 |
* translation is done, so we need to translate here */ |
| 3067 |
if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec != AST_FORMAT_SLINEAR)) { |
| 3068 |
if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) { |
| 3069 |
f = &ast_null_frame; |
| 3070 |
ao2_ref(details, -1); |
| 3071 |
return f; |
| 3072 |
} |
| 3073 |
} |
| 3074 |
|
| 3075 |
/* XXX we ignore the return value here, perhaps we should |
| 3076 |
* disable the gateway if a write fails. I am not sure how a |
| 3077 |
* write would fail, or even if a failure would be fatal so for |
| 3078 |
* now we'll just ignore the return value. */ |
| 3079 |
gateway->s->tech->write(gateway->s, f); |
| 3080 |
f = &ast_null_frame; |
| 3081 |
ao2_ref(details, -1); |
| 3082 |
return f; |
| 3083 |
} |
| 3084 |
|
| 3085 |
/* force silence on the line if T.38 negotiation might be taking place */ |
| 3086 |
if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) { |
| 3087 |
if (f->frametype == AST_FRAME_VOICE && f->subclass.codec == AST_FORMAT_SLINEAR) { |
| 3088 |
short silence_buf[f->samples]; |
| 3089 |
struct ast_frame silence_frame = { |
| 3090 |
.frametype = AST_FRAME_VOICE, |
| 3091 |
.data.ptr = silence_buf, |
| 3092 |
.samples = f->samples, |
| 3093 |
.datalen = sizeof(silence_buf), |
| 3094 |
.subclass.codec = AST_FORMAT_SLINEAR, |
| 3095 |
}; |
| 3096 |
memset(silence_buf, 0, sizeof(silence_buf)); |
| 3097 |
|
| 3098 |
ao2_ref(details, -1); |
| 3099 |
return ast_frisolate(&silence_frame); |
| 3100 |
} else { |
| 3101 |
ao2_ref(details, -1); |
| 3102 |
return &ast_null_frame; |
| 3103 |
} |
| 3104 |
} |
| 3105 |
|
| 3106 |
ao2_ref(details, -1); |
| 3107 |
return f; |
| 3108 |
} |
| 3109 |
|
| 3110 |
/*! \brief Attach a gateway framehook object to a channel. |
| 3111 |
* \param chan the channel to attach to |
| 3112 |
* \param details fax session details |
| 3113 |
* \return the framehook id of the attached framehook or -1 on error |
| 3114 |
* \retval -1 error |
| 3115 |
*/ |
| 3116 |
static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details) |
| 3117 |
{ |
| 3118 |
struct fax_gateway *gateway; |
| 3119 |
struct ast_framehook_interface fr_hook = { |
| 3120 |
.version = AST_FRAMEHOOK_INTERFACE_VERSION, |
| 3121 |
.event_cb = fax_gateway_framehook, |
| 3122 |
.destroy_cb = fax_gateway_framehook_destroy, |
| 3123 |
}; |
| 3124 |
|
| 3125 |
ast_string_field_set(details, result, "SUCCESS"); |
| 3126 |
ast_string_field_set(details, resultstr, "gateway operation started successfully"); |
| 3127 |
ast_string_field_set(details, error, "NO_ERROR"); |
| 3128 |
set_channel_variables(chan, details); |
| 3129 |
|
| 3130 |
/* set up the frame hook*/ |
| 3131 |
gateway = fax_gateway_new(details); |
| 3132 |
if (!gateway) { |
| 3133 |
ast_string_field_set(details, result, "FAILED"); |
| 3134 |
ast_string_field_set(details, resultstr, "error initializing gateway session"); |
| 3135 |
ast_string_field_set(details, error, "INIT_ERROR"); |
| 3136 |
set_channel_variables(chan, details); |
| 3137 |
report_fax_status(chan, details, "No Available Resource"); |
| 3138 |
return -1; |
| 3139 |
} |
| 3140 |
|
| 3141 |
fr_hook.data = gateway; |
| 3142 |
ast_channel_lock(chan); |
| 3143 |
gateway->framehook = ast_framehook_attach(chan, &fr_hook); |
| 3144 |
ast_channel_unlock(chan); |
| 3145 |
|
| 3146 |
if (gateway->framehook < 0) { |
| 3147 |
ao2_ref(gateway, -1); |
| 3148 |
ast_string_field_set(details, result, "FAILED"); |
| 3149 |
ast_string_field_set(details, resultstr, "error attaching gateway to channel"); |
| 3150 |
ast_string_field_set(details, error, "INIT_ERROR"); |
| 3151 |
set_channel_variables(chan, details); |
| 3152 |
return -1; |
| 3153 |
} |
| 3154 |
|
| 3155 |
return gateway->framehook; |
| 3156 |
} |
| 3157 |
|
| 3158 |
/*! \brief Faxdetect loop used by WaitFAX |
| 3159 |
* \details Run DSP faxdetect on the channel for timeout seconds or until fax is detected |
| 3160 |
* \param chan channel to run fax detect on |
| 3161 |
* \param timeout maximum time to wait for fax detection |
| 3162 |
* \return 0 if nothing was detected 1 on CNG detected 2 if T38 negotiation is started -1 on error*/ |
| 3163 |
static int do_waitfax_exec(struct ast_channel *chan, int timeout, int noiselim) |
| 3164 |
{ |
| 3165 |
int timeleft = timeout; |
| 3166 |
int res = 0; |
| 3167 |
int dspnoise = 0; |
| 3168 |
struct ast_dsp *dsp = NULL; |
| 3169 |
struct ast_frame *f, *dup_f; |
| 3170 |
enum ast_t38_state t38state = T38_STATE_UNKNOWN; |
| 3171 |
unsigned int orig_read_format; |
| 3172 |
AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; |
| 3173 |
|
| 3174 |
/* Setup DSP CNG processing */ |
| 3175 |
orig_read_format = chan->readformat; |
| 3176 |
switch (chan->readformat) { |
| 3177 |
case AST_FORMAT_SLINEAR: |
| 3178 |
break; |
| 3179 |
default: |
| 3180 |
if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { |
| 3181 |
return -1; |
| 3182 |
} |
| 3183 |
} |
| 3184 |
|
| 3185 |
if (!(dsp = ast_dsp_new())) { |
| 3186 |
return -1; |
| 3187 |
} |
| 3188 |
|
| 3189 |
ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT); |
| 3190 |
ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_SQUELCH); |
| 3191 |
ast_dsp_set_threshold(dsp, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE)); |
| 3192 |
|
| 3193 |
AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames); |
| 3194 |
|
| 3195 |
while ((timeleft = ast_waitfor(chan, timeleft))) { |
| 3196 |
if (!(f = ast_read(chan))) { |
| 3197 |
break; |
| 3198 |
} |
| 3199 |
|
| 3200 |
if (dsp && (f->frametype == AST_FRAME_VOICE) && ((f->subclass.codec == AST_FORMAT_SLINEAR) || |
| 3201 |
(f->subclass.codec == AST_FORMAT_ULAW) || (f->subclass.codec == AST_FORMAT_ALAW))) { |
| 3202 |
f = ast_dsp_process(chan, dsp, f); |
| 3203 |
if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == 'f')) { |
| 3204 |
res = 1; |
| 3205 |
} else if ((f->frametype == AST_FRAME_VOICE) && (noiselim > 0)) { |
| 3206 |
ast_dsp_noise(dsp, f, &dspnoise); |
| 3207 |
if (dspnoise > noiselim) { |
| 3208 |
break; |
| 3209 |
} |
| 3210 |
} |
| 3211 |
} |
| 3212 |
|
| 3213 |
if (ast_is_deferrable_frame(f)) { |
| 3214 |
AST_LIST_INSERT_HEAD(&deferred_frames, f, frame_list); |
| 3215 |
} else { |
| 3216 |
ast_frfree(f); |
| 3217 |
} |
| 3218 |
|
| 3219 |
t38state = ast_channel_get_t38_state(chan); |
| 3220 |
if ((t38state == T38_STATE_NEGOTIATING) || (t38state == T38_STATE_NEGOTIATED)) { |
| 3221 |
res = 2; |
| 3222 |
break; |
| 3223 |
} else if (res) { |
| 3224 |
break; |
| 3225 |
} |
| 3226 |
} |
| 3227 |
|
| 3228 |
if (orig_read_format != chan->readformat) { |
| 3229 |
ast_set_read_format(chan, orig_read_format); |
| 3230 |
} |
| 3231 |
|
| 3232 |
ast_channel_lock(chan); |
| 3233 |
while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) { |
| 3234 |
dup_f = ast_frisolate(f); |
| 3235 |
ast_queue_frame_head(chan, dup_f); |
| 3236 |
if (dup_f != f) { |
| 3237 |
ast_frfree(f); |
| 3238 |
} |
| 3239 |
} |
| 3240 |
ast_channel_unlock(chan); |
| 3241 |
|
| 3242 |
if (dsp) { |
| 3243 |
ast_dsp_free(dsp); |
| 3244 |
} |
| 3245 |
|
| 3246 |
return res; |
| 3247 |
} |
| 3248 |
|
| 3249 |
/*! \brief Alternate wait app that listens for CNG |
| 3250 |
* \details This app answers the channel and waits for CNG tone or T38 negotiation |
| 3251 |
* if the channel driver supports faxdetect it will proceed to the fax |
| 3252 |
* extension. |
| 3253 |
* it will set FAXOPT(status) and FAXOPT(statusstr) allowing dial plan processing where |
| 3254 |
* the channel does not have fax detect or the Dialplan needs to handle faxdetection |
| 3255 |
* |
| 3256 |
* \param chan channel this application is called on |
| 3257 |
* \param data the paramaters passed to the application |
| 3258 |
* \return this application will always return 0 |
| 3259 |
*/ |
| 3260 |
static int waitfax_exec(struct ast_channel *chan, const char *data) |
| 3261 |
{ |
| 3262 |
int res = 0; |
| 3263 |
unsigned int timeout; |
| 3264 |
unsigned int noiselim; |
| 3265 |
int ptres = -1; |
| 3266 |
char *parse; |
| 3267 |
struct ast_fax_session_details *details; |
| 3268 |
struct ast_silence_generator *silgen = NULL; |
| 3269 |
struct ast_tone_zone_sound *ts; |
| 3270 |
|
| 3271 |
AST_DECLARE_APP_ARGS(args, |
| 3272 |
AST_APP_ARG(timeout); |
| 3273 |
AST_APP_ARG(tone); |
| 3274 |
AST_APP_ARG(noise); |
| 3275 |
); |
| 3276 |
|
| 3277 |
if (!(details = find_or_create_details(chan))) { |
| 3278 |
ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n"); |
| 3279 |
return 0; |
| 3280 |
} |
| 3281 |
|
| 3282 |
parse = ast_strdupa(data); |
| 3283 |
AST_STANDARD_APP_ARGS(args, parse); |
| 3284 |
|
| 3285 |
if (!ast_strlen_zero(args.timeout) && sscanf(args.timeout, "%u", &timeout) == 1) { |
| 3286 |
timeout = timeout * 1000; |
| 3287 |
} else { |
| 3288 |
timeout = 0; |
| 3289 |
} |
| 3290 |
|
| 3291 |
if (timeout <= 0) { |
| 3292 |
ast_string_field_set(details, result, "ERROR"); |
| 3293 |
ast_string_field_set(details, resultstr, "Invalid timeout in WaitFAX"); |
| 3294 |
ast_log(LOG_ERROR, "Application WaitFAX requires a valid timeout\n"); |
| 3295 |
ao2_ref(details, -1); |
| 3296 |
return 0; |
| 3297 |
} |
| 3298 |
|
| 3299 |
if (ast_strlen_zero(args.noise) || sscanf(args.noise, "%u", &noiselim) != 1) { |
| 3300 |
noiselim = 0; |
| 3301 |
} |
| 3302 |
|
| 3303 |
if (chan->_state != AST_STATE_UP) { |
| 3304 |
ast_answer(chan); |
| 3305 |
} |
| 3306 |
|
| 3307 |
/* If no other generator is present, start tone or silencegen while waiting */ |
| 3308 |
if (!chan->generatordata && !ast_strlen_zero(args.tone)) { |
| 3309 |
if ((ts = ast_get_indication_tone(chan->zone, args.tone))) { |
| 3310 |
ptres = ast_playtones_start(chan, 0, ts->data, 0); |
| 3311 |
ts = ast_tone_zone_sound_unref(ts); |
| 3312 |
} else { |
| 3313 |
ptres = ast_playtones_start(chan, 0, args.tone, 0); |
| 3314 |
} |
| 3315 |
if (!ptres && ast_opt_transmit_silence) { |
| 3316 |
silgen = ast_channel_start_silence_generator(chan); |
| 3317 |
} |
| 3318 |
} else if (ast_opt_transmit_silence && !chan->generatordata) { |
| 3319 |
silgen = ast_channel_start_silence_generator(chan); |
| 3320 |
} |
| 3321 |
|
| 3322 |
res = do_waitfax_exec(chan, timeout, noiselim); |
| 3323 |
|
| 3324 |
/* stop silgen or tones if present */ |
| 3325 |
if (silgen) { |
| 3326 |
ast_channel_stop_silence_generator(chan, silgen); |
| 3327 |
} else if (!ptres) { |
| 3328 |
ast_playtones_stop(chan); |
| 3329 |
} |
| 3330 |
|
| 3331 |
if (res > 0) { |
| 3332 |
ast_string_field_set(details, result, "SUCCESS"); |
| 3333 |
if (res == 1) { |
| 3334 |
ast_string_field_set(details, resultstr, "CNG"); |
| 3335 |
} else { |
| 3336 |
ast_string_field_set(details, resultstr, "T38"); |
| 3337 |
} |
| 3338 |
} else if (res < 0) { |
| 3339 |
ast_string_field_set(details, result, "ERROR"); |
| 3340 |
ast_string_field_set(details, resultstr, "DSP Error WaitFAX Failed"); |
| 3341 |
} else { |
| 3342 |
ast_string_field_set(details, result, "FAILED"); |
| 3343 |
ast_string_field_set(details, resultstr, "No CNG Tone Or T38 Detected"); |
| 3344 |
} |
| 3345 |
|
| 3346 |
ao2_ref(details, -1); |
| 3347 |
return 0; |
| 3348 |
} |
| 3349 |
|
| 3350 |
|
| 2290 |
/*! \brief hash callback for ao2 */ |
3351 |
/*! \brief hash callback for ao2 */ |
| 2291 |
static int session_hash_cb(const void *obj, const int flags) |
3352 |
static int session_hash_cb(const void *obj, const int flags) |
| 2292 |
{ |
3353 |
{ |
|
Lines 2558-2576
Link Here
|
| 2558 |
while ((s = ao2_iterator_next(&i))) { |
3619 |
while ((s = ao2_iterator_next(&i))) { |
| 2559 |
ao2_lock(s); |
3620 |
ao2_lock(s); |
| 2560 |
|
3621 |
|
| 2561 |
if (!(filenames = generate_filenames_string(s->details, "", ", "))) { |
3622 |
filenames = generate_filenames_string(s->details, "", ", "); |
| 2562 |
ast_log(LOG_ERROR, "error printing filenames for 'fax show sessions' command"); |
|
|
| 2563 |
ao2_unlock(s); |
| 2564 |
ao2_ref(s, -1); |
| 2565 |
ao2_iterator_destroy(&i); |
| 2566 |
return CLI_FAILURE; |
| 2567 |
} |
| 2568 |
|
3623 |
|
| 2569 |
ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n", |
3624 |
ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n", |
| 2570 |
s->channame, s->tech->type, s->id, |
3625 |
s->channame, s->tech->type, s->id, |
| 2571 |
(s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38", |
3626 |
(s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38", |
| 2572 |
(s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", |
3627 |
(s->details->caps & AST_FAX_TECH_GATEWAY) |
| 2573 |
ast_fax_state_to_str(s->state), filenames); |
3628 |
? "gateway" |
|
|
3629 |
: (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", |
| 3630 |
ast_fax_state_to_str(s->state), S_OR(filenames, "")); |
| 2574 |
|
3631 |
|
| 2575 |
ast_free(filenames); |
3632 |
ast_free(filenames); |
| 2576 |
ao2_unlock(s); |
3633 |
ao2_unlock(s); |
|
Lines 2689-2694
Link Here
|
| 2689 |
} |
3746 |
} |
| 2690 |
if (!strcasecmp(data, "ecm")) { |
3747 |
if (!strcasecmp(data, "ecm")) { |
| 2691 |
ast_copy_string(buf, details->option.ecm ? "yes" : "no", len); |
3748 |
ast_copy_string(buf, details->option.ecm ? "yes" : "no", len); |
|
|
3749 |
} else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") || |
| 3750 |
!strcasecmp(data, "t38_gateway") || !strcasecmp(data, "faxgateway")) { |
| 3751 |
ast_copy_string(buf, details->gateway_id != -1 ? "yes" : "no", len); |
| 2692 |
} else if (!strcasecmp(data, "error")) { |
3752 |
} else if (!strcasecmp(data, "error")) { |
| 2693 |
ast_copy_string(buf, details->error, len); |
3753 |
ast_copy_string(buf, details->error, len); |
| 2694 |
} else if (!strcasecmp(data, "filename")) { |
3754 |
} else if (!strcasecmp(data, "filename")) { |
|
Lines 2763-2768
Link Here
|
| 2763 |
} else { |
3823 |
} else { |
| 2764 |
ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(ecm).\n", value); |
3824 |
ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(ecm).\n", value); |
| 2765 |
} |
3825 |
} |
|
|
3826 |
} else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") || |
| 3827 |
!strcasecmp(data, "t38_gateway") || !strcasecmp(data, "faxgateway")) { |
| 3828 |
const char *val = ast_skip_blanks(value); |
| 3829 |
char *timeout = strchr(val, ','); |
| 3830 |
|
| 3831 |
if (timeout) { |
| 3832 |
*timeout++ = '\0'; |
| 3833 |
} |
| 3834 |
|
| 3835 |
if (ast_true(val)) { |
| 3836 |
if (details->gateway_id < 0) { |
| 3837 |
details->gateway_timeout = 0; |
| 3838 |
if (timeout) { |
| 3839 |
unsigned int gwtimeout; |
| 3840 |
if (sscanf(timeout, "%u", &gwtimeout) == 1) { |
| 3841 |
details->gateway_timeout = gwtimeout * 1000; |
| 3842 |
} else { |
| 3843 |
ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n", timeout, data); |
| 3844 |
} |
| 3845 |
} |
| 3846 |
|
| 3847 |
details->gateway_id = fax_gateway_attach(chan, details); |
| 3848 |
if (details->gateway_id < 0) { |
| 3849 |
ast_log(LOG_ERROR, "Error attaching T.38 gateway to channel %s.\n", chan->name); |
| 3850 |
res = -1; |
| 3851 |
} else { |
| 3852 |
ast_debug(1, "Attached T.38 gateway to channel %s.\n", chan->name); |
| 3853 |
} |
| 3854 |
} else { |
| 3855 |
ast_log(LOG_WARNING, "Attempt to attach a T.38 gateway on channel (%s) with gateway already running.\n", chan->name); |
| 3856 |
} |
| 3857 |
} else if (ast_false(val)) { |
| 3858 |
ast_framehook_detach(chan, details->gateway_id); |
| 3859 |
details->gateway_id = -1; |
| 3860 |
} else { |
| 3861 |
ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data); |
| 3862 |
} |
| 2766 |
} else if (!strcasecmp(data, "headerinfo")) { |
3863 |
} else if (!strcasecmp(data, "headerinfo")) { |
| 2767 |
ast_string_field_set(details, headerinfo, value); |
3864 |
ast_string_field_set(details, headerinfo, value); |
| 2768 |
} else if (!strcasecmp(data, "localstationid")) { |
3865 |
} else if (!strcasecmp(data, "localstationid")) { |
|
Lines 2813-2818
Link Here
|
| 2813 |
ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_receivefax); |
3910 |
ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_receivefax); |
| 2814 |
} |
3911 |
} |
| 2815 |
|
3912 |
|
|
|
3913 |
if (ast_unregister_application(app_waitfax) < 0) { |
| 3914 |
ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_waitfax); |
| 3915 |
} |
| 3916 |
|
| 2816 |
if (fax_logger_level != -1) { |
3917 |
if (fax_logger_level != -1) { |
| 2817 |
ast_logger_unregister_level("FAX"); |
3918 |
ast_logger_unregister_level("FAX"); |
| 2818 |
} |
3919 |
} |
|
Lines 2852-2859
Link Here
|
| 2852 |
ao2_ref(faxregistry.container, -1); |
3953 |
ao2_ref(faxregistry.container, -1); |
| 2853 |
return AST_MODULE_LOAD_DECLINE; |
3954 |
return AST_MODULE_LOAD_DECLINE; |
| 2854 |
} |
3955 |
} |
|
|
3956 |
|
| 3957 |
if (ast_register_application_xml(app_waitfax, waitfax_exec) < 0) { |
| 3958 |
ast_log(LOG_WARNING, "failed to register '%s'.\n", app_waitfax); |
| 3959 |
} |
| 3960 |
|
| 2855 |
ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli)); |
3961 |
ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli)); |
| 2856 |
res = ast_custom_function_register(&acf_faxopt); |
3962 |
res = ast_custom_function_register(&acf_faxopt); |
| 2857 |
fax_logger_level = ast_logger_register_level("FAX"); |
3963 |
fax_logger_level = ast_logger_register_level("FAX"); |
| 2858 |
|
3964 |
|
| 2859 |
return res; |
3965 |
return res; |