[asterisk-commits] mnicholson: branch irroot/t38gateway-trunk r324474 - /team/irroot/t38gateway-...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jun 22 09:50:56 CDT 2011


Author: mnicholson
Date: Wed Jun 22 09:50:52 2011
New Revision: 324474

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=324474
Log:
This is a rewrite of the T.38 negotiation portion of the framehook code.

Things have been cleaned up a bit and code was added to handle as many
different T.38 negotiation scenarios as possible. Code was also added to
automatically disable the framehook when starting the gateway fails or when
T.38 negotiation fails. The gateway should now also automatically disable
itself if both channels support T.38 and T.38 negotiation is successful.

Now CED tones are only handled if the side receiving the CED tone indicates
that it supports T.38. A CED tone will trigger the gateway to send a T.38
reinvite to the party sending the fax.

Modified:
    team/irroot/t38gateway-trunk/res/res_fax.c

Modified: team/irroot/t38gateway-trunk/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-trunk/res/res_fax.c?view=diff&rev=324474&r1=324473&r2=324474
==============================================================================
--- team/irroot/t38gateway-trunk/res/res_fax.c (original)
+++ team/irroot/t38gateway-trunk/res/res_fax.c Wed Jun 22 09:50:52 2011
@@ -260,7 +260,9 @@
 	/*! framehook used in gateway mode */
 	int framehook;
 	/*! bridged*/
-	int bridged;
+	int bridged:1;
+	/*! a flag to track the state of our negotiation */
+	enum ast_t38_state t38_state;
 	/*Original audio formats*/
 	struct ast_format chan_read_format;
 	struct ast_format chan_write_format;
@@ -2368,76 +2370,238 @@
 	return 0;
 }
 
+static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+	struct ast_frame *dfr = ast_frdup(f);
+	struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
+	struct ast_channel *other = (active == chan) ? peer : chan;
+
+	if (!dfr) {
+		return f;
+	}
+
+	if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
+		return f;
+	}
+
+	if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'e') {
+		if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
+			struct ast_control_t38_parameters t38_parameters = {
+				.request_response = AST_T38_REQUEST_NEGOTIATE,
+			};
+			struct ast_frame control_frame = {
+				.src = "res_fax",
+				.frametype = AST_FRAME_CONTROL,
+				.datalen = sizeof(t38_parameters),
+				.subclass.integer = AST_CONTROL_T38_PARAMETERS,
+				.data.ptr = &t38_parameters,
+			};
+
+			struct ast_fax_session_details *details = find_details(chan);
+			ast_frfree(dfr);
+
+			if (!details) {
+				ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+				ast_framehook_detach(chan, details->gateway_id);
+				details->gateway_id = -1;
+				return &ast_null_frame;
+			}
+
+			t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+			ao2_ref(details, -1);
+
+			if (!(f = ast_frisolate(&control_frame))) {
+				ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name);
+				return &ast_null_frame;
+			}
+
+			gateway->t38_state = T38_STATE_NEGOTIATING;
+
+			ast_debug(1, "detected CED tone on %s, requesting T.38 on %s for T.38 gateway session\n", active->name, other->name);
+			return f;
+		} else {
+			ast_debug(1, "detected CED tone on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
+		}
+	}
+
+	ast_frfree(dfr);
+	return f;
+}
+
+static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel *active, struct ast_control_t38_parameters *control_params)
+{
+	if (active == chan) {
+		return ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params));
+	} else {
+		return ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params));
+	}
+}
+
 /*! \brief T38 Gateway Negotiate t38 parameters
+ * \param gateway gateway object
  * \param chan channel running the gateway
  * \param peer channel im bridged too
  * \param active channel the frame originated on
  * \param f the control frame to process
- * \return processed control frame or null frame*/
-static struct ast_frame *ast_t38_gateway_parameters(struct fax_gateway *gateway, struct ast_channel *chan,struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
-{
-	struct ast_control_t38_parameters *active_param;
-	struct ast_fax_t38_parameters *t38_param;
-	struct ast_channel *inactive;
-	enum ast_t38_state activestate, inactivestate;
+ * \return processed control frame or null frame
+ */
+static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+	struct ast_control_t38_parameters *control_params = f->data.ptr;
+	struct ast_channel *other = (active == chan) ? peer : chan;
 	struct ast_fax_session_details *details;
 
-	/* Get a FAX session details structure from the channel's FAX datastore*/
+	if (f->datalen != sizeof(struct ast_control_t38_parameters)) {
+		/* invalaid AST_CONTROL_T38_PARAMETERS frame, we can't
+		 * do anything with it, pass it on */
+		return f;
+	}
+
+	/* ignore frames from ourselves */
+	if ((gateway->t38_state == T38_STATE_NEGOTIATED && control_params->request_response == AST_T38_NEGOTIATED)
+		|| (gateway->t38_state == T38_STATE_REJECTED && control_params->request_response == AST_T38_REFUSED)
+		|| (gateway->t38_state == T38_STATE_NEGOTIATING && control_params->request_response == AST_T38_REQUEST_TERMINATE)) {
+
+		return f;
+	}
+
 	if (!(details = find_details(chan))) {
+		ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
 		return f;
 	}
 
-	/* T.38 States*/
-	inactive = (active == chan) ? peer : chan;
-	activestate = ast_channel_get_t38_state(active);
-	inactivestate = ast_channel_get_t38_state(inactive);
-	t38_param = (active == chan) ? &details->our_t38_parameters : &details->their_t38_parameters;
-
-	active_param = f->data.ptr;
-	switch (active_param->request_response) {
-		/* ill need to do something inteligent with AST_T38_REQUEST_TERMINATE/AST_T38_TERMINATED like turn off frame hooks and reply */
-		case AST_T38_REFUSED:
-			/* if T.38 was refused, activate the gateway and pretend it never happened */
-			ast_debug(1, "%s refused to negotiate T.38, gateway will fake success for %s\n", active->name, inactive->name);
-			t38_parameters_fax_to_ast(active_param, t38_param);
-			active_param->request_response = AST_T38_NEGOTIATED;
-			break;
-		case AST_T38_REQUEST_NEGOTIATE:
-		case AST_T38_NEGOTIATED:
-			if ((inactivestate == T38_STATE_UNAVAILABLE) || (inactivestate == T38_STATE_REJECTED)) {
-				/* im not capable of T.38 so use the options from FAXOPT and queue on other side for me (faking it)*/
-				ast_debug(1, "%s on %s [%i] %s I: %s [%i]\n",
-					(active_param->request_response == AST_T38_NEGOTIATED) ? "Negotiated" : "Request",
-					active->name, activestate, (activestate == T38_STATE_NEGOTIATED) ? "Ignoring" : "Queueing",
-					inactive->name, inactivestate);
-				t38_parameters_fax_to_ast(active_param, t38_param);
-				/*prevent a negotiation storm only talk to unstable ends*/
-				if ((activestate == T38_STATE_NEGOTIATING) || (activestate == T38_STATE_UNKNOWN)) {
-					ast_queue_control_data(inactive, AST_CONTROL_T38_PARAMETERS, active_param, sizeof(struct ast_control_t38_parameters));
-				}
-				f = &ast_null_frame;
+	if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) {
+		enum ast_t38_state state = ast_channel_get_t38_state(other);
+		if (state == T38_STATE_UNKNOWN) {
+			/* we detected a request to negotiate T.38 and the
+			 * other channel appears to support T.38, we'll pass
+			 * the request through and only step in if the other
+			 * channel rejects the request */
+			ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", active->name, other->name);
+			gateway->t38_state = T38_STATE_UNKNOWN;
+			ao2_ref(details, -1);
+			return f;
+		} else if (state == T38_STATE_UNAVAILABLE || state == T38_STATE_REJECTED) {
+			/* the other channel does not support T.38, we need to
+			 * step in here */
+			ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", active->name, other->name);
+			ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+
+			t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
+
+			if (fax_gateway_start(gateway, details, chan)) {
+				ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+				gateway->t38_state = T38_STATE_REJECTED;
+				control_params->request_response = AST_T38_REFUSED;
+
+				ast_framehook_detach(chan, details->gateway_id);
+				details->gateway_id = -1;
 			} else {
-				/*im capable of T.38 make the paramaters mine*/
-				ast_debug(1, "%s on %s [%i] Storing I: %s [%i]\n",
-					(active_param->request_response == AST_T38_NEGOTIATED) ? "Negotiated" : "Request",
-					active->name, activestate, inactive->name, inactivestate);
-				t38_parameters_ast_to_fax(t38_param, active_param);
+				gateway->t38_state = T38_STATE_NEGOTIATED;
+				control_params->request_response = AST_T38_NEGOTIATED;
 			}
-			break;
-		default:
-			ast_debug(1, "Unhandled Request Response [%i] on %s [%i] I: %s [%i]\n", active_param->request_response,
-				active->name, activestate, inactive->name, inactivestate);
-			break;
-	}
-
-	/* Start gateway if both channels are in a stable T.38 (not negotiating) state and only one of them is not negotiated! */
-	if (((activestate == T38_STATE_NEGOTIATED) && ((inactivestate == T38_STATE_UNAVAILABLE) || (inactivestate == T38_STATE_REJECTED))) ||
-	    ((inactivestate == T38_STATE_NEGOTIATED) && ((activestate == T38_STATE_UNAVAILABLE) || (activestate == T38_STATE_REJECTED))))  {
-		if (!fax_gateway_start(gateway, details, chan)) {
-			ast_verb(3, "T.38 Gateway starting for chan %s and peer %s\n", chan->name, peer->name);
-		}
-		/* XXX if starting the fax gateway fails, we should probably
-		 * tear down the T.38 session */
+
+			/* XXX check return value? */
+			fax_gateway_indicate_t38(chan, active, control_params);
+
+			ao2_ref(details, -1);
+			return &ast_null_frame;
+		} else {
+			ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", active->name, other->name);
+			ao2_ref(details, -1);
+			return f;
+		}
+	} else if (gateway->t38_state == T38_STATE_NEGOTIATING
+		&& control_params->request_response == AST_T38_REFUSED) {
+
+		ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", active->name);
+
+		/* our request to negotiate T.38 was refused, nothing more we
+		 * can do */
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
+	} else if (gateway->t38_state == T38_STATE_NEGOTIATING
+		&& control_params->request_response == AST_T38_NEGOTIATED) {
+
+		ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+
+		t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+
+		if (fax_gateway_start(gateway, details, chan)) {
+			ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+			gateway->t38_state = T38_STATE_NEGOTIATING;
+			control_params->request_response = AST_T38_REQUEST_TERMINATE;
+
+			/* XXX check return value? */
+			fax_gateway_indicate_t38(chan, active, control_params);
+		} else {
+			gateway->t38_state = T38_STATE_NEGOTIATED;
+		}
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
+	} else if (control_params->request_response == AST_T38_REFUSED) {
+		/* the other channel refused the request to negotiate T.38,
+		 * we'll step in here and pretend the request was accepted */
+
+		ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", other->name, active->name);
+		ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", other->name, active->name);
+
+		t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
+
+		if (fax_gateway_start(gateway, details, chan)) {
+			ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+			gateway->t38_state = T38_STATE_REJECTED;
+			control_params->request_response = AST_T38_REFUSED;
+
+			ast_framehook_detach(chan, details->gateway_id);
+			details->gateway_id = -1;
+		} else {
+			gateway->t38_state = T38_STATE_NEGOTIATED;
+			control_params->request_response = AST_T38_NEGOTIATED;
+		}
+
+		ao2_ref(details, -1);
+		return f;
+	} else if (control_params->request_response == AST_T38_REQUEST_TERMINATE) {
+		/* the channel wishes to end our short relationship, we shall
+		 * oblige */
+
+		ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", active->name);
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		gateway->t38_state = T38_STATE_REJECTED;
+		control_params->request_response = AST_T38_TERMINATED;
+
+		/* XXX check return value? */
+		fax_gateway_indicate_t38(chan, active, control_params);
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
+	} else if (control_params->request_response == AST_T38_NEGOTIATED) {
+		ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", active->name, other->name);
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		ao2_ref(details, -1);
+		return f;
+	} else if (control_params->request_response == AST_T38_TERMINATED) {
+		ast_debug(1, "T.38 disabled on channel %s\n", active->name);
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
 	}
 
 	ao2_ref(details, -1);
@@ -2450,6 +2614,9 @@
 	struct fax_gateway *gateway = data;
 
 	if (gateway->s) {
+		if (gateway->s->tech->cancel_session) {
+			gateway->s->tech->cancel_session(gateway->s);
+		}
 		ao2_lock(faxregistry.container);
 		ao2_unlink(faxregistry.container, gateway->s);
 		ao2_unlock(faxregistry.container);
@@ -2470,14 +2637,8 @@
  * \param data framehook data (gateway)
  * \return processed frame or NULL when f is NULL or a null frame*/
 static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
-	struct ast_control_t38_parameters *t38_parameters;
-	enum ast_t38_state		t38state;
-	enum ast_t38_state		t38pstate;
-	struct fax_gateway	*gateway = data;
-	struct ast_fax_session		*s;
-	struct ast_frame		*dfr = NULL;
-	struct ast_channel		*peer, *active, *inactive;
-	struct ast_dsp			*dsp;
+	struct fax_gateway *gateway = data;
+	struct ast_channel *peer, *active;
 
 	/* restore audio formats when we are detached */
 	if (event == AST_FRAMEHOOK_EVENT_DETACHED && gateway->bridged) {
@@ -2524,9 +2685,6 @@
 		ast_channel_make_compatible(chan, peer);
 		gateway->bridged = 1;
 	}
-
-	s = gateway->s;
-
 
 	/* only handle VOICE, MODEM, and CONTROL frames*/
 	switch (f->frametype) {
@@ -2541,7 +2699,7 @@
 			}
 			break;
 		case AST_FRAME_MODEM:
-			if (s && (f->subclass.integer == AST_MODEM_T38)) {
+			if (f->subclass.integer == AST_MODEM_T38) {
 				break;
 			}
 			return f;
@@ -2569,84 +2727,31 @@
 
 	/* handle control frames */
 	if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
-		if (s || (f->datalen != sizeof(struct ast_control_t38_parameters))) {
-			/* invalaid frame AST_CONTROL_T38_PARAMETERS frame */
-			return &ast_null_frame;
-		}
-
-		return ast_t38_gateway_parameters(gateway, chan,peer,active,f);
+		return fax_gateway_detect_t38(gateway, chan, peer, active, f);
 	}
 
 	/* not in gateway mode yet, listen for CED */
-	dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
-	if (!s && chan && peer && dsp && (f->frametype == AST_FRAME_VOICE)) {
-		dfr = ast_frdup(f);
-		dfr = ast_dsp_process(active, dsp, dfr);
-		if (dfr && (dfr->frametype == AST_FRAME_DTMF) && (dfr->subclass.integer == 'e')) {
-			inactive = (chan == active) ? peer : chan;
-			t38state = ast_channel_get_t38_state(active);
-			t38pstate = ast_channel_get_t38_state(inactive);
-			t38_parameters = &our_t38_parameters;
-			t38_parameters->request_response = AST_T38_REQUEST_NEGOTIATE;
-			/* queue the negotiation on a channel with no  T.38 to get the other side up and going*/
-			if ((t38state == T38_STATE_UNAVAILABLE) || (t38state == T38_STATE_REJECTED)) {
-				ast_queue_control_data(active, AST_CONTROL_T38_PARAMETERS, t38_parameters, sizeof(our_t38_parameters));
-			} else if ((t38pstate == T38_STATE_UNAVAILABLE) || (t38pstate == T38_STATE_REJECTED)) {
-				ast_queue_control_data(inactive, AST_CONTROL_T38_PARAMETERS, t38_parameters, sizeof(our_t38_parameters));
+	/* XXX this should detect a v21 preamble instead of CED */
+	if (gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
+		return fax_gateway_detect_ced(gateway, chan, peer, active, f);
+	}
+
+	/* in gateway mode, gateway some packets */
+	if (gateway->t38_state == T38_STATE_NEGOTIATED) {
+		/* framehooks are called in __ast_read() before frame format
+		 * translation is does, so we need to translate here */
+		if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id != AST_FORMAT_SLINEAR)) {
+			if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) {
+				f = &ast_null_frame;
+				return f;
 			}
-			ast_debug(1 ,"Got Fax Tone CED Chan %s [%i] Sending T.38 Params Peer Is %s [%i]\n",
-				active->name, t38state, (inactive) ? inactive->name : "NONE", t38pstate);
-			if (gateway->chan_dsp) {
-				ast_dsp_free(gateway->chan_dsp);
-				gateway->chan_dsp = NULL;
-			}
-			if (gateway->peer_dsp) {
-				ast_dsp_free(gateway->peer_dsp);
-				gateway->peer_dsp = NULL;
-			}
-		}
-		if (dfr) {
-			ast_frfree(dfr);
-		}
+		}
+
+		/* XXX perhaps we should check the return value of
+		 * tech->write() */
+		gateway->s->tech->write(gateway->s, f);
+		f = &ast_null_frame;
 		return f;
-	} else if (!s || !peer) {
-		return f;
-	}
-
-	/*get t38states i may not be bridged yet [these should be stored on gw start to minimise this hook]*/
-	t38state = ast_channel_get_t38_state(active);
-	t38pstate = ast_channel_get_t38_state((active == chan) ? peer : chan);
-
-	/*Im not a gateway and this is not audio channel*/
-	if ((t38state == T38_STATE_NEGOTIATED) && (t38pstate != T38_STATE_UNAVAILABLE) &&
-	    (t38pstate != T38_STATE_REJECTED)) {
-		return f;
-	}
-
-	switch(t38state) {
-		case T38_STATE_UNAVAILABLE:
-		case T38_STATE_REJECTED:
-			/*Im not a gateway and this is a audio channel*/
-			if (t38pstate != T38_STATE_NEGOTIATED) {
-				break;
-			}
-			/* i need to translate here as this is done latter in __ast_read*/
-			if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id != AST_FORMAT_SLINEAR)) {
-				if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) {
-					if (!f) {
-						f = &ast_null_frame;
-					}
-					break;
-				}
-			}
-		case T38_STATE_NEGOTIATED:
-			/* XXX perhaps we should check the return value of
-			 * tech->write() */
-			s->tech->write(s, f);
-			f = &ast_null_frame;
-			break;
-		default:
-			ast_debug(1, "Incompatible T.38 State for gateway mode\n");
 	}
 
 	return f;




More information about the asterisk-commits mailing list