summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/dsp.h5
-rw-r--r--include/asterisk/res_fax.h4
-rw-r--r--main/dsp.c148
-rw-r--r--res/res_fax.c193
-rw-r--r--res/res_fax_spandsp.c85
5 files changed, 207 insertions, 228 deletions
diff --git a/include/asterisk/dsp.h b/include/asterisk/dsp.h
index f82f08a94..16262c05d 100644
--- a/include/asterisk/dsp.h
+++ b/include/asterisk/dsp.h
@@ -45,9 +45,8 @@
#define DSP_FAXMODE_DETECT_CNG (1 << 0)
#define DSP_FAXMODE_DETECT_CED (1 << 1)
-#define DSP_FAXMODE_DETECT_V21 (1 << 2)
-#define DSP_FAXMODE_DETECT_SQUELCH (1 << 3)
-#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21)
+#define DSP_FAXMODE_DETECT_SQUELCH (1 << 2)
+#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED)
#define DSP_TONE_STATE_SILENCE 0
#define DSP_TONE_STATE_RINGING 1
diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h
index ba20944e4..b397bd445 100644
--- a/include/asterisk/res_fax.h
+++ b/include/asterisk/res_fax.h
@@ -44,6 +44,8 @@ enum ast_fax_capabilities {
AST_FAX_TECH_MULTI_DOC = (1 << 4),
/*! T.38 - T.30 Gateway */
AST_FAX_TECH_GATEWAY = (1 << 5),
+ /*! V21 detection is supported */
+ AST_FAX_TECH_V21_DETECT = (1 << 6),
};
/*! \brief fax modem capabilities */
@@ -160,6 +162,8 @@ struct ast_fax_session_details {
uint32_t send_cng:1;
/*! send a T.38 reinvite */
uint32_t request_t38:1;
+ /*! a V.21 preamble was detected */
+ uint32_t v21_detected:1;
};
} option;
/*! override the minimum transmission rate with a channel variable */
diff --git a/main/dsp.c b/main/dsp.c
index 91eea6c9d..0f8955314 100644
--- a/main/dsp.c
+++ b/main/dsp.c
@@ -246,20 +246,6 @@ typedef struct
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 */
@@ -405,7 +391,6 @@ struct ast_dsp {
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)
@@ -479,56 +464,10 @@ static void ast_tone_detect_init(tone_detect_state_t *s, int freq, int duration,
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, unsigned int sample_rate)
-{
- 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 = (20 * sample_rate) / 1000;
-
- 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, sample_rate);
-
- 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, s->sample_rate);
ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB, s->sample_rate);
- ast_v21_detect_init(&s->v21_state, s->sample_rate);
if (s->faxmode & DSP_FAXMODE_DETECT_SQUELCH) {
s->cng_tone_state.squelch = 1;
s->ced_tone_state.squelch = 1;
@@ -580,89 +519,6 @@ static void ast_digit_detect_init(digit_detect_state_t *s, int mf, unsigned int
}
}
-/*! \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->miss_count == 3 || (s->hit_count == 1 && s->miss_count == 2)) {
- s->hit_count++;
- } else {
- s->hit_count = 1;
- }
-
- 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;
@@ -1599,10 +1455,6 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
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)) {
diff --git a/res/res_fax.c b/res/res_fax.c
index ec2c98b82..7ee4cceff 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -249,13 +249,12 @@ struct ast_fax_debug_info {
struct fax_gateway {
/*! \brief FAX Session */
struct ast_fax_session *s;
+ struct ast_fax_session *peer_v21_session;
+ struct ast_fax_session *chan_v21_session;
/*! \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 */
@@ -336,7 +335,7 @@ static struct {
enum ast_fax_modems modems;
uint32_t statusevents:1;
uint32_t ecm:1;
- unsigned int minrate;
+ unsigned int minrate;
unsigned int maxrate;
} general_options;
@@ -371,7 +370,7 @@ struct manager_event_info {
};
static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame)
-{
+{
struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c;
int dspsilence;
unsigned int last_consec_frames, last_consec_ms;
@@ -403,7 +402,7 @@ static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned in
history->consec_ms += (frame->samples / 8);
}
-static void destroy_callback(void *data)
+static void destroy_callback(void *data)
{
if (data) {
ao2_ref(data, -1);
@@ -421,9 +420,9 @@ static struct ast_fax_session_details *find_details(struct ast_channel *chan)
struct ast_fax_session_details *details;
struct ast_datastore *datastore;
- ast_channel_lock(chan);
+ ast_channel_lock(chan);
if (!(datastore = ast_channel_datastore_find(chan, &fax_datastore, NULL))) {
- ast_channel_unlock(chan);
+ ast_channel_unlock(chan);
return NULL;
}
if (!(details = datastore->data)) {
@@ -431,8 +430,8 @@ static struct ast_fax_session_details *find_details(struct ast_channel *chan)
ast_channel_unlock(chan);
return NULL;
}
- ao2_ref(details, 1);
- ast_channel_unlock(chan);
+ ao2_ref(details, 1);
+ ast_channel_unlock(chan);
return details;
}
@@ -442,11 +441,11 @@ static void destroy_session_details(void *details)
{
struct ast_fax_session_details *d = details;
struct ast_fax_document *doc;
-
+
while ((doc = AST_LIST_REMOVE_HEAD(&d->documents, next))) {
ast_free(doc);
}
- ast_string_field_free_memory(d);
+ ast_string_field_free_memory(d);
}
/*! \brief create a FAX session details structure */
@@ -457,7 +456,7 @@ static struct ast_fax_session_details *session_details_new(void)
if (!(d = ao2_alloc(sizeof(*d), destroy_session_details))) {
return NULL;
}
-
+
if (ast_string_field_init(d, 512)) {
ao2_ref(d, -1);
return NULL;
@@ -512,7 +511,7 @@ static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, co
}
/*! \brief returns a reference counted details structure from the channel's fax datastore. If the datastore
- * does not exist it will be created */
+ * does not exist it will be created */
static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan)
{
struct ast_fax_session_details *details;
@@ -556,7 +555,7 @@ unsigned int ast_fax_minrate(void)
}
static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
-{
+{
char *m[5], *tok, *v = (char *)value;
int i = 0, j;
@@ -636,7 +635,13 @@ static char *ast_fax_caps_to_str(enum ast_fax_capabilities caps, char *buf, size
ast_build_string(&buf, &size, "GATEWAY");
first = 0;
}
-
+ if (caps & AST_FAX_TECH_V21_DETECT) {
+ if (!first) {
+ ast_build_string(&buf, &size, ",");
+ }
+ ast_build_string(&buf, &size, "V21");
+ first = 0;
+ }
return out;
}
@@ -748,7 +753,7 @@ void ast_fax_tech_unregister(struct ast_fax_tech *tech)
ast_module_unref(ast_module_info->self);
ast_free(fax);
ast_verb(4, "Unregistered FAX module type '%s'\n", tech->type);
- break;
+ break;
}
AST_RWLIST_TRAVERSE_SAFE_END;
AST_RWLIST_UNLOCK(&faxmodules);
@@ -856,7 +861,7 @@ static void destroy_session(void *session)
}
ao2_ref(s->details, -1);
}
-
+
if (s->debug_info) {
ast_dsp_free(s->debug_info->dsp);
ast_free(s->debug_info);
@@ -996,7 +1001,7 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
return NULL;
}
ast_dsp_set_threshold(s->debug_info->dsp, 128);
- }
+ }
if (!(s->channame = ast_strdup(chan->name))) {
fax_session_release(s, token);
@@ -1311,7 +1316,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
}
ast_channel_lock(chan);
- /* update session details */
+ /* update session details */
if (ast_strlen_zero(details->headerinfo) && (tempvar = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"))) {
ast_string_field_set(details, headerinfo, tempvar);
}
@@ -1409,7 +1414,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
(frame->datalen == sizeof(t38_parameters))) {
unsigned int was_t38 = t38negotiated;
struct ast_control_t38_parameters *parameters = frame->data.ptr;
-
+
switch (parameters->request_response) {
case AST_T38_REQUEST_NEGOTIATE:
/* the other end has requested a switch to T.38, so reply that we are willing, if we can
@@ -1435,15 +1440,15 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
ast_smoother_free(fax->smoother);
fax->smoother = NULL;
}
-
+
report_fax_status(chan, details, "T.38 Negotiated");
-
+
ast_verb(3, "Channel '%s' switched to T.38 FAX session '%d'.\n", chan->name, fax->id);
}
} else if ((frame->frametype == expected_frametype) &&
(!memcmp(&frame->subclass, &expected_framesubclass, sizeof(frame->subclass)))) {
struct ast_frame *f;
-
+
if (fax->smoother) {
/* push the frame into a smoother */
if (ast_smoother_feed(fax->smoother, frame) < 0) {
@@ -1805,7 +1810,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
ao2_ref(details, -1);
return -1;
}
-
+
ast_atomic_fetchadd_int(&faxregistry.fax_rx_attempts, 1);
pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel Problems");
@@ -1909,7 +1914,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
get_manager_event_info(chan, &info);
manager_event(EVENT_FLAG_CALL,
- "ReceiveFAX",
+ "ReceiveFAX",
"Channel: %s\r\n"
"Context: %s\r\n"
"Exten: %s\r\n"
@@ -2265,7 +2270,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
ao2_ref(details, -1);
return -1;
}
-
+
/* check for unsupported FAX application options */
if (ast_test_flag(&opts, OPT_CALLERMODE) || ast_test_flag(&opts, OPT_CALLEDMODE)) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
@@ -2407,7 +2412,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
ast_channel_lock(chan);
get_manager_event_info(chan, &info);
manager_event(EVENT_FLAG_CALL,
- "SendFAX",
+ "SendFAX",
"Channel: %s\r\n"
"Context: %s\r\n"
"Exten: %s\r\n"
@@ -2439,20 +2444,34 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
return (!channel_alive) ? -1 : 0;
}
-/*! \brief destroy a FAX gateway session structure */
-static void destroy_gateway(void *data)
+/*! \brief destroy the v21 detection parts of a fax gateway session */
+static void destroy_v21_sessions(struct fax_gateway *gateway)
{
- struct fax_gateway *gateway = data;
+ if (gateway->chan_v21_session) {
+ ao2_lock(faxregistry.container);
+ ao2_unlink(faxregistry.container, gateway->chan_v21_session);
+ ao2_unlock(faxregistry.container);
- if (gateway->chan_dsp) {
- ast_dsp_free(gateway->chan_dsp);
- gateway->chan_dsp = NULL;
+ ao2_ref(gateway->chan_v21_session, -1);
+ gateway->chan_v21_session = NULL;
}
- if (gateway->peer_dsp) {
- ast_dsp_free(gateway->peer_dsp);
- gateway->peer_dsp = NULL;
+ if (gateway->peer_v21_session) {
+ ao2_lock(faxregistry.container);
+ ao2_unlink(faxregistry.container, gateway->peer_v21_session);
+ ao2_unlock(faxregistry.container);
+
+ ao2_ref(gateway->peer_v21_session, -1);
+ gateway->peer_v21_session = NULL;
}
+}
+
+/*! \brief destroy a FAX gateway session structure */
+static void destroy_gateway(void *data)
+{
+ struct fax_gateway *gateway = data;
+
+ destroy_v21_sessions(gateway);
if (gateway->s) {
fax_session_release(gateway->s, gateway->token);
@@ -2468,35 +2487,38 @@ static void destroy_gateway(void *data)
}
/*! \brief Create a new fax gateway object.
+ * \param chan the channel the gateway object will be attached to
* \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)
+static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details)
{
struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
+ struct ast_fax_session_details *v21_details;
if (!gateway) {
return NULL;
}
- gateway->chan_dsp = ast_dsp_new();
- if (!gateway->chan_dsp) {
+ if (!(v21_details = session_details_new())) {
ao2_ref(gateway, -1);
return NULL;
}
- gateway->peer_dsp = ast_dsp_new();
- if (!gateway->peer_dsp) {
+ v21_details->caps = AST_FAX_TECH_V21_DETECT;
+ if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
+ ao2_ref(v21_details, -1);
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);
+ if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
+ ao2_ref(v21_details, -1);
+ ao2_ref(gateway, -1);
+ return NULL;
+ }
+ ao2_ref(v21_details, -1);
- ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
- ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21);
+ gateway->framehook = -1;
details->caps = AST_FAX_TECH_GATEWAY;
if (details->gateway_timeout && !(gateway->s = fax_session_reserve(details, &gateway->token))) {
@@ -2591,24 +2613,20 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
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;
+ struct ast_fax_session *active_v21_session = (active == chan) ? gateway->chan_v21_session : gateway->peer_v21_session;
- if (gateway->detected_v21) {
+ if (!active_v21_session || gateway->detected_v21) {
return f;
}
- if (!dfr) {
- return f;
- }
-
- if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
- return f;
+ if (active_v21_session->tech->write(active_v21_session, f) == 0 &&
+ active_v21_session->details->option.v21_detected) {
+ gateway->detected_v21 = 1;
}
- if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') {
- gateway->detected_v21 = 1;
+ if (gateway->detected_v21) {
+ destroy_v21_sessions(gateway);
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);
@@ -2617,7 +2635,6 @@ static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, str
}
}
- ast_frfree(dfr);
return f;
}
@@ -3120,7 +3137,7 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d
set_channel_variables(chan, details);
/* set up the frame hook*/
- gateway = fax_gateway_new(details);
+ gateway = fax_gateway_new(chan, details);
if (!gateway) {
ast_string_field_set(details, result, "FAILED");
ast_string_field_set(details, resultstr, "error initializing gateway session");
@@ -3468,7 +3485,7 @@ static char *cli_fax_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_
switch (cmd) {
case CLI_INIT:
e->command = "fax set debug {on|off}";
- e->usage =
+ e->usage =
"Usage: fax set debug { on | off }\n"
" Enable/Disable FAX debugging on new FAX sessions. The basic FAX debugging will result in\n"
" additional events sent to manager sessions with 'call' class permissions. When\n"
@@ -3499,11 +3516,11 @@ static char *cli_fax_show_capabilities(struct ast_cli_entry *e, int cmd, struct
{
struct fax_module *fax;
unsigned int num_modules = 0;
-
+
switch (cmd) {
case CLI_INIT:
e->command = "fax show capabilities";
- e->usage =
+ e->usage =
"Usage: fax show capabilities\n"
" Shows the capabilities of the registered FAX technology modules\n";
return NULL;
@@ -3594,12 +3611,12 @@ static char *cli_fax_show_session(struct ast_cli_entry *e, int cmd, struct ast_c
return CLI_SUCCESS;
}
-
+
/*! \brief display fax stats */
static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct fax_module *fax;
-
+
switch (cmd) {
case CLI_INIT:
e->command = "fax show stats";
@@ -3628,6 +3645,36 @@ static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli
return CLI_SUCCESS;
}
+static const char *cli_session_type(struct ast_fax_session *s)
+{
+ if (s->details->caps & AST_FAX_TECH_AUDIO) {
+ return "G.711";
+ }
+ if (s->details->caps & AST_FAX_TECH_T38) {
+ return "T.38";
+ }
+
+ return "none";
+}
+
+static const char *cli_session_operation(struct ast_fax_session *s)
+{
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ return "gateway";
+ }
+ if (s->details->caps & AST_FAX_TECH_SEND) {
+ return "send";
+ }
+ if (s->details->caps & AST_FAX_TECH_RECEIVE) {
+ return "receive";
+ }
+ if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+ return "V.21";
+ }
+
+ return "none";
+}
+
/*! \brief display fax sessions */
static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -3658,10 +3705,8 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
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_GATEWAY)
- ? "gateway"
- : (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
+ cli_session_type(s),
+ cli_session_operation(s),
ast_fax_state_to_str(s->state), S_OR(filenames, ""));
ast_free(filenames);
@@ -3693,9 +3738,9 @@ static int set_config(const char *config_file)
struct ast_flags config_flags = { 0 };
char modems[128] = "";
- /* set defaults */
+ /* set defaults */
general_options.minrate = RES_FAX_MINRATE;
- general_options.maxrate = RES_FAX_MAXRATE;
+ general_options.maxrate = RES_FAX_MAXRATE;
general_options.statusevents = RES_FAX_STATUSEVENTS;
general_options.modems = RES_FAX_MODEM;
general_options.ecm = AST_FAX_OPTFLAG_TRUE;
@@ -3979,7 +4024,7 @@ struct ast_custom_function acf_faxopt = {
static int unload_module(void)
{
ast_cli_unregister_multiple(fax_cli, ARRAY_LEN(fax_cli));
-
+
if (ast_custom_function_unregister(&acf_faxopt) < 0) {
ast_log(LOG_WARNING, "failed to unregister function '%s'\n", acf_faxopt.name);
}
@@ -4012,7 +4057,7 @@ static int load_module(void)
if (!(faxregistry.container = ao2_container_alloc(FAX_MAXBUCKETS, session_hash_cb, session_cmp_cb))) {
return AST_MODULE_LOAD_DECLINE;
}
-
+
if (set_config(config) < 0) {
ast_log(LOG_ERROR, "failed to load configuration file '%s'\n", config);
ao2_ref(faxregistry.container, -1);
diff --git a/res/res_fax_spandsp.c b/res/res_fax_spandsp.c
index 81711bb40..7f4fbfc67 100644
--- a/res/res_fax_spandsp.c
+++ b/res/res_fax_spandsp.c
@@ -80,6 +80,9 @@ 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 int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f);
+static void spandsp_v21_cleanup(struct ast_fax_session *s);
+static void spandsp_v21_tone(void *data, int code, int level, int delay);
static char *spandsp_fax_cli_show_capabilities(int fd);
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
@@ -98,7 +101,9 @@ static struct ast_fax_tech spandsp_fax_tech = {
*/
.version = "pre-20090220",
#endif
- .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY,
+ .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND
+ | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY
+ | AST_FAX_TECH_V21_DETECT,
.new_session = spandsp_fax_new,
.destroy_session = spandsp_fax_destroy,
.read = spandsp_fax_read,
@@ -150,8 +155,12 @@ struct spandsp_pvt {
struct ast_timer *timer;
AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
+
+ int v21_detected;
+ modem_connect_tones_rx_state_t *tone_state;
};
+static int spandsp_v21_new(struct spandsp_pvt *p);
static void session_destroy(struct spandsp_pvt *p);
static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count);
static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code);
@@ -450,6 +459,20 @@ static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *deta
t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
}
+static int spandsp_v21_new(struct spandsp_pvt *p)
+{
+ /* XXX Here we use MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE even though
+ * we don't care about CED tones. Using MODEM_CONNECT_TONES_PREAMBLE
+ * doesn't seem to work right all the time.
+ */
+ p->tone_state = modem_connect_tones_rx_init(NULL, MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE, spandsp_v21_tone, p);
+ if (!p->tone_state) {
+ return -1;
+ }
+
+ return 0;
+}
+
/*! \brief create an instance of the spandsp tech_pvt for a fax session */
static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token)
{
@@ -461,6 +484,15 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
goto e_return;
}
+ if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+ if (spandsp_v21_new(p)) {
+ ast_log(LOG_ERROR, "Cannot initialize the spandsp private v21 technology structure.\n");
+ goto e_return;
+ }
+ s->state = AST_FAX_STATE_ACTIVE;
+ return p;
+ }
+
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
s->state = AST_FAX_STATE_INITIALIZED;
return p;
@@ -513,6 +545,11 @@ e_return:
return NULL;
}
+static void spandsp_v21_cleanup(struct ast_fax_session *s) {
+ struct spandsp_pvt *p = s->tech_pvt;
+ modem_connect_tones_rx_free(p->tone_state);
+}
+
/*! \brief Destroy a spandsp fax session.
*/
static void spandsp_fax_destroy(struct ast_fax_session *s)
@@ -521,6 +558,8 @@ static void spandsp_fax_destroy(struct ast_fax_session *s)
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
spandsp_fax_gateway_cleanup(s);
+ } else if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+ spandsp_v21_cleanup(s);
} else {
session_destroy(p);
}
@@ -571,6 +610,36 @@ static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s)
return &ast_null_frame;
}
+static void spandsp_v21_tone(void *data, int code, int level, int delay)
+{
+ struct spandsp_pvt *p = data;
+
+ if (code == MODEM_CONNECT_TONES_FAX_PREAMBLE) {
+ p->v21_detected = 1;
+ }
+}
+
+static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f) {
+ struct spandsp_pvt *p = s->tech_pvt;
+
+ if (p->v21_detected) {
+ return 0;
+ }
+
+ /*invalid frame*/
+ if (!f->data.ptr || !f->datalen) {
+ return -1;
+ }
+
+ modem_connect_tones_rx(p->tone_state, f->data.ptr, f->samples);
+
+ if (p->v21_detected) {
+ s->details->option.v21_detected = 1;
+ }
+
+ return 0;
+}
+
/*! \brief Write a frame to the spandsp fax stack.
* \param s a fax session
* \param f the frame to write
@@ -585,6 +654,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
{
struct spandsp_pvt *p = s->tech_pvt;
+ if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+ return spandsp_v21_detect(s, f);
+ }
+
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
return spandsp_fax_gateway_process(s, f);
}
@@ -906,10 +979,10 @@ static char *spandsp_fax_cli_show_capabilities(int fd)
/*! \brief */
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
{
- struct spandsp_pvt *p = s->tech_pvt;
-
ao2_lock(s);
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ struct spandsp_pvt *p = s->tech_pvt;
+
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));
@@ -920,7 +993,13 @@ static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
}
+ } else if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+ ast_cli(fd, "%-22s : %d\n", "session", s->id);
+ ast_cli(fd, "%-22s : %s\n", "operation", "V.21 Detect");
+ ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
} else {
+ struct spandsp_pvt *p = s->tech_pvt;
+
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));