[svn-commits] mmichelson: branch group/CCSS r221659 - in /team/group/CCSS: apps/ include/as...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Oct 1 11:52:16 CDT 2009


Author: mmichelson
Date: Thu Oct  1 11:52:12 2009
New Revision: 221659

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=221659
Log:
Move dial_cc_interfaces datastore out of app_dial and into ccss.c

This commit is pretty much a giant move operation to put some code that
was only in app_dial into ccss.c. There are two really good reasons for
doing this:

1. If it were ever decided that more than just the Dial application should
allow for CC operations, then having a central location for the datastore
and a set of API calls to get and set data makes the task much easier.

2. More importantly, the addition of API calls to interact with the 
datastore allows for other files outside of app_dial to get and
set items. This allows for, say, chan_local to set data before Dial is
even called so that we can know if things like "/nb" are at the end
of the channel name. It also will allow for called channel drivers to
query what the core ID of the call for which they have just queued a
CC frame will be. This will require a bit more modification, actually.

The next commit will be to fix up the comments in the moved code, change
some function names so they make more sense, and doxygenify all new API
calls I've added in this commit.


Modified:
    team/group/CCSS/apps/app_dial.c
    team/group/CCSS/include/asterisk/ccss.h
    team/group/CCSS/main/ccss.c

Modified: team/group/CCSS/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/team/group/CCSS/apps/app_dial.c?view=diff&rev=221659&r1=221658&r2=221659
==============================================================================
--- team/group/CCSS/apps/app_dial.c (original)
+++ team/group/CCSS/apps/app_dial.c Thu Oct  1 11:52:12 2009
@@ -584,9 +584,6 @@
 };
 
 static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode);
-static void cc_set_done_flag(struct ast_channel *chan);
-static void handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data);
-static int dial_create_new_cc_core(struct ast_channel *inbound);
 
 static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
 {
@@ -785,7 +782,7 @@
 			 * dialed for CC purposes. Setting the done flag will ensure that
 			 * any Dial operations that happen later won't record CC interfaces.
 			 */
-			cc_set_done_flag(o->chan);
+			ast_ignore_cc(o->chan);
 			ast_log(LOG_NOTICE, "Since we're forwarding to %s, we will not accept CC offers from him\n", o->chan->name);
 		} else
 			ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
@@ -942,7 +939,7 @@
 				ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
 			}
 			*to = 0;
-			dial_create_new_cc_core(in);
+			ast_cc_create_new_core(in);
 			return NULL;
 		}
 		winner = ast_waitfor_n(watchers, pos, to);
@@ -992,7 +989,7 @@
 						/* This channel is forwarding the call, and is capable of CC, so
 						 * be sure to add the new device interface to the list
 						 */
-						handle_cc_control_frame(in, c, f->data.ptr);
+						ast_handle_cc_control_frame(in, c, f->data.ptr);
 					}
 					ast_frfree(f);
 				}
@@ -1161,7 +1158,7 @@
 					break;
 				case AST_CONTROL_CC:
 					if (!ignore_cc) {
-						handle_cc_control_frame(in, c, f->data.ptr);
+						ast_handle_cc_control_frame(in, c, f->data.ptr);
 					}
 					break;
 				case -1:
@@ -1213,7 +1210,7 @@
 					}
 					ast_frfree(f);
 				}
-				dial_create_new_cc_core(in);
+				ast_cc_create_new_core(in);
 				return NULL;
 			}
 
@@ -1231,7 +1228,7 @@
 						strcpy(pa->status, "CANCEL");
 						ast_frfree(f);
 						ast_channel_unlock(in);
-						dial_create_new_cc_core(in);
+						ast_cc_create_new_core(in);
 						return NULL;
 					}
 					ast_channel_unlock(in);
@@ -1244,7 +1241,7 @@
 					strcpy(pa->status, "CANCEL");
 					ast_cdr_noanswer(in->cdr);
 					ast_frfree(f);
-					dial_create_new_cc_core(in);
+					ast_cc_create_new_core(in);
 					return NULL;
 				}
 			}
@@ -1287,7 +1284,7 @@
 	}
 #endif
 
-	dial_create_new_cc_core(in);
+	ast_cc_create_new_core(in);
 	return peer;
 }
 
@@ -1566,474 +1563,6 @@
 	return 1; /* success */
 }
 
-/*!
- * This counter is used for assigning unique ids
- * to CC-enabled dialed interfaces.
- */
-static int dial_cc_interface_counter;
-
-/*!
- * \brief data stored in CC datastore
- *
- * The datastore creates a list of interfaces that were
- * dialed, including both extensions and devices. In addition
- * to the intrinsic data of the tree, some extra information
- * is needed for use by app_dial.
- */
-struct dial_cc_interfaces {
-	/*!
-	 * This value serves a dual-purpose. When dial starts, if the
-	 * dial_cc_interfaces datastore currently exists on the calling
-	 * channel, then the dial_parent_id will serve as a means of
-	 * letting the new extension ast_cc_tree_item we create know
-	 * who his parent is. This value will be the extension
-	 * ast_cc_tree_item that dialed the local channel that resulted
-	 * in the new Dial app being called.
-	 *
-	 * In addition, once an extension ast_cc_tree_item is created,
-	 * the dial_parent_id will be changed to the id of that newly
-	 * created interface. This way, device interfaces created from
-	 * receiving AST_CONTROL_CC frames can use this field to determine
-	 * who their parent extension interface should be.
-	 */
-	unsigned int dial_parent_id;
-	/*!
-	 * When a new Dial application is started, and the datastore
-	 * already exists on the channel, we can determine if we
-	 * should be adding any new interface information to tree.
-	 */
-	char done;
-	/*!
-	 * When nested dials occur (i.e. dialing local channels), we need
-	 * to be sure that they will not try to create a core instance themselves.
-	 * Instead, they need to pass the frame up to the parent dialing application.
-	 */
-	char nested_dial;
-	/*!
-	 * When nested dials occur (i.e. dialing local channels), the invocation
-	 * of the Local channel may have modifiers like /n or /b on the channel name.
-	 * This needs to be preserved as the dialable name on an extension tree item
-	 * which may be created for the local channel.
-	 */
-	char *current_extension_dialable_name;
-	/*!
-	 * Reference-counted "tree" of interfaces.
-	 */
-	struct ast_cc_interface_tree *interface_tree;
-};
-
-/*!
- * \brief Destructor function for cc_interfaces datastore
- *
- * This function will free the actual datastore and drop
- * the refcount for the interface tree by one. In cases
- * where CC can actually be used, this unref will not
- * result in the destruction of the interface tree, because
- * the CC core will still have a reference.
- *
- * \param data The dial_cc_interfaces struct to destroy
- */
-static void dial_cc_interfaces_destroy(void *data)
-{
-	struct dial_cc_interfaces *cc_interfaces = data;
-	ao2_t_ref(cc_interfaces->interface_tree, -1, "Unref dial's ref to interface tree");
-	ast_free(cc_interfaces);
-}
-
-/*!
- * \brief Duplicate callback for cc_interfaces datastore
- *
- * Integers are copied by value, but the interface tree
- * is done via a shallow copy and a bump of the refcount.
- * This way, sub-Dials will be appending interfaces onto
- * the same list as this call to Dial.
- *
- * \param data The old dial_cc_interfaces we want to copy
- * \retval NULL Could not allocate memory for new dial_cc_interfaces
- * \retval non-NULL The new copy of the dial_cc_interfaces
- */
-static void *dial_cc_interfaces_duplicate(void *data)
-{
-	struct dial_cc_interfaces *old_cc_interfaces = data;
-	struct dial_cc_interfaces *new_cc_interfaces = ast_calloc(1, sizeof(*new_cc_interfaces));
-	if (!new_cc_interfaces) {
-		return NULL;
-	}
-	new_cc_interfaces->done = old_cc_interfaces->done;
-	new_cc_interfaces->dial_parent_id = old_cc_interfaces->dial_parent_id;
-	new_cc_interfaces->nested_dial = 1;
-	ao2_t_ref(old_cc_interfaces->interface_tree, +1, "New ref due to duplication of interface tree");
-	new_cc_interfaces->interface_tree = old_cc_interfaces->interface_tree;
-	return new_cc_interfaces;
-}
-
-/*!
- * \brief information regarding the dial_cc_interfaces datastore
- *
- * The dial_cc_interfaces datastore is responsible for keeping track
- * of what CC-enabled interfaces have been dialed by the caller. For
- * more information regarding the actual structure of the tree, see
- * the documentation provided in include/asterisk/ccss.h
- */
-static const struct ast_datastore_info dial_cc_interfaces_info = {
-	.type = "Dial CC Interfaces",
-	.duplicate = dial_cc_interfaces_duplicate,
-	.destroy = dial_cc_interfaces_destroy,
-};
-
-/*!
- * \brief Routine to set the done flag on the dial_cc_interfaces datastore
- *
- * When we determine that further calls to Dial in a particular thread should
- * not pay attention to any new CC offers, we set this flag as an indicator.
- * 
- * \param chan The calling channel with teh dial_cc_interfaaces datastore on it
- * \return void
- */
-static void cc_set_done_flag(struct ast_channel *chan)
-{
-	struct ast_datastore *cc_datastore;
-	struct dial_cc_interfaces *cc_interfaces;
-
-	ast_channel_lock(chan);
-	if ((cc_datastore = ast_channel_datastore_find(chan, &dial_cc_interfaces_info, NULL))) {
-		cc_interfaces = cc_datastore->data;
-		cc_interfaces->done = 1;
-	}
-	ast_channel_unlock(chan);
-}
-
-/*!
- * \brief Allocate and initialize an "extension" interface for CC purposes
- *
- * When app_dial starts, this function is called in order to set up the
- * information about the extension in which this Dial is occurring. Any
- * devices dialed will have this particular ast_cc_tree_item as a parent.
- *
- * \param exten Extension from which Dial is occurring
- * \param context Context to which exten belongs
- * \param parent_id What should we set the parent_id of this interface to?
- * \retval NULL Memory allocation failure
- * \retval non-NULL The newly-created ast_cc_tree_item for the extension
- */
-static struct ast_cc_tree_item *cc_extension_tree_item_init(const char * const exten, const char * const context, const unsigned int parent_id)
-{
-	struct ast_str *str = ast_str_alloca(2 * AST_MAX_EXTENSION);
-	struct ast_cc_interface *cc_interface;
-	struct ast_cc_tree_item *tree_item;
-
-	ast_str_set(&str, 0, "%s@%s", exten, context);
-
-	if (!(cc_interface = ao2_alloc(sizeof(*cc_interface) + ast_str_strlen(str), ast_cc_interface_destroy))) {
-		return NULL;
-	}
-
-	if (!(tree_item = ast_calloc(1, sizeof(*tree_item)))) {
-		ao2_t_ref(cc_interface, -1, "failed to allocate the tree item, so unref the interface");
-		return NULL;
-	}
-
-	tree_item->id = ast_atomic_fetchadd_int(&dial_cc_interface_counter, +1);
-	tree_item->parent_id = parent_id;
-	cc_interface->monitor_type = "extension";
-	cc_interface->monitor_class = AST_CC_EXTENSION_MONITOR;
-	strcpy(cc_interface->name, ast_str_buffer(str));
-	tree_item->interface = cc_interface;
-	ast_log(LOG_NOTICE, "Created an extension cc interface for '%s' with id %d and parent %d\n", cc_interface->name, tree_item->id, tree_item->parent_id);
-	return tree_item;
-}
-
-/*!
- * \brief allocate dial_cc_interfaces datastore and initialize fields
- *
- * This function is called when Situation 1 occurs in create_root_tree_item.
- * See that function for more information on what Situation 1 is.
- *
- * In this particular case, we have to do a lot of memory allocation in order
- * to create the datastore, the data for the datastore, the tree of interfaces
- * that we'll be adding to, and the initial extension interface for this Dial
- * attempt.
- *
- * \param chan The channel onto which the datastore should be added.
- * \retval -1 An error occurred
- * \retval 0 Success
- */
-static int cc_interfaces_datastore_init(struct ast_channel *chan) {
-	struct dial_cc_interfaces *interfaces;
-	struct ast_cc_tree_item *tree_item;
-	struct ast_datastore *dial_cc_datastore;
-
-	if (!(interfaces = ast_calloc(1, sizeof(*interfaces)))) {
-		return -1;
-	}
-
-	if (!(tree_item = cc_extension_tree_item_init(S_OR(chan->macroexten, chan->exten), S_OR(chan->macrocontext, chan->context), 0))) {
-		ast_free(interfaces);
-		return -1;
-	}
-
-	if (!(dial_cc_datastore = ast_datastore_alloc(&dial_cc_interfaces_info, NULL))) {
-		ast_cc_tree_item_destroy(tree_item);
-		ast_free(interfaces);
-		return -1;
-	}
-
-	if (!(interfaces->interface_tree = ao2_t_alloc(sizeof(*interfaces->interface_tree), ast_cc_interface_tree_destroy,
-					"Allocate interface tree"))) {
-		ast_datastore_free(dial_cc_datastore);
-		ast_cc_tree_item_destroy(tree_item);
-		ast_free(interfaces);
-		return -1;
-	}
-
-	/* Finally, all that allocation is done... */
-	AST_LIST_HEAD_INIT(interfaces->interface_tree);
-	AST_LIST_INSERT_TAIL(interfaces->interface_tree, tree_item, next);
-	dial_cc_datastore->data = interfaces;
-	dial_cc_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
-	interfaces->dial_parent_id = tree_item->id;
-	ast_channel_lock(chan);
-	ast_channel_datastore_add(chan, dial_cc_datastore);
-	ast_channel_unlock(chan);
-	return 0;
-}
-
-/*!
- * \brief Allocate and intitialize a device ast_cc_tree_item
- *
- * For all intents and purposes, this is the same as
- * cc_extension_tree_item_init, except that there is only
- * a single parameter used for naming the interface.
- *
- * This function is called when handling AST_CONTROL_CC frames.
- * The device has reported that CC is possible, so we add it
- * to the interface_tree.
- *
- * Note that it is not necessarily erroneous to add the same
- * device to the tree twice. If the same device is called by
- * two different extension during the same call, then
- * that is a legitimate situation. Of course, I'm pretty sure
- * the dialed_interfaces global datastore will not allow that
- * to happen anyway.
- *
- * \param name The name of the device being added to the tree
- * \param name_len The length of name, in bytes
- * \param parent_id The parent of this new tree node.
- * \retval NULL Memory allocation failure
- * \retval non-NULL The new ast_cc_interface created.
- */
-static struct ast_cc_tree_item *cc_device_tree_item_init(const char * const name, const size_t name_len,
-		const int parent_id, const struct ast_control_cc_payload *cc_data)
-{
-	struct ast_cc_interface *cc_interface = ao2_alloc(sizeof(*cc_interface) + name_len, ast_cc_interface_destroy);
-	struct ast_cc_tree_item *tree_item;
-
-	if (!cc_interface) {
-		return NULL;
-	}
-
-	if (!(cc_interface->config_params = ast_cc_config_params_init())) {
-		ao2_t_ref(cc_interface, -1, "Failed to allocate config params, unref interface");
-		return NULL;
-	}
-
-	if (!(tree_item = ast_calloc(1, sizeof(*tree_item)))) {
-		ao2_t_ref(cc_interface, -1, "Failed to allocate tree item, unref interface");
-		return NULL;
-	}
-
-	strcpy(cc_interface->name, name);
-	tree_item->id = ast_atomic_fetchadd_int(&dial_cc_interface_counter, +1);
-	tree_item->parent_id = parent_id;
-	tree_item->service_offered = cc_data->service;
-	cc_interface->monitor_type = cc_data->monitor_type;
-	cc_interface->monitor_class = AST_CC_DEVICE_MONITOR;
-	tree_item->interface = cc_interface;
-	ast_cc_copy_config_params(cc_interface->config_params, cc_data->config_params);
-	ast_log(LOG_NOTICE, "Created a device cc interface for '%s' with id %d and parent %d\n", cc_interface->name, tree_item->id, tree_item->parent_id);
-	return tree_item;
-}
-
-/*!
- * \brief Function for handling AST_CONTROL_CC frames
- *
- * Unless we are ignoring CC for some reason, we will always
- * call this function when we read an AST_CONTROL_CC frame
- * from an outbound channel.
- *
- * This function will call cc_device_tree_item_init to
- * create the new ast_cc_tree_item for the device from which
- * we read the frame. In addition, the new device will be added
- * to the interface tree on the dial_cc_interfaces datastore
- * on the inbound channel.
- *
- * If this is the first AST_CONTROL_CC frame that we have handled
- * for this call, then we will also initialize the CC core for
- * this call.
- *
- * \param inbound The caller's channel
- * \param outbound The channel we read the control frame from
- * \param frame_data The data on the frame (XXX currently unused)
- * \return void
- */
-static void handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data)
-{
-	char device_name[AST_CHANNEL_NAME];
-	char *dash;
-	struct ast_cc_tree_item *tree_item;
-	struct ast_datastore *cc_datastore;
-	struct dial_cc_interfaces *cc_interfaces;
-	struct ast_control_cc_payload *cc_data = frame_data;
-
-	ast_copy_string(device_name, outbound->name, sizeof(device_name));
-	if ((dash = strrchr(device_name, '-'))) {
-		*dash = '\0';
-	}
-
-	if (!(cc_datastore = ast_channel_datastore_find(inbound, &dial_cc_interfaces_info, NULL))) {
-		ast_log(LOG_WARNING, "Unable to retrieve CC datastore while processing CC frame from '%s'. CC services will be unavailable.\n", device_name);
-		return;
-	}
-
-	cc_interfaces = cc_datastore->data;
-
-	/* Yeah this kind of sucks, but luckily most people
-	 * aren't dialing thousands of interfaces on every call
-	 *
-	 * This traversal helps us to not create duplicate tree items in
-	 * case a device queues multiple CC control frames.
-	 */
-	AST_LIST_LOCK(cc_interfaces->interface_tree);
-	AST_LIST_TRAVERSE(cc_interfaces->interface_tree, tree_item, next) {
-		if (!strcmp(tree_item->interface->name, device_name)) {
-			ast_log(LOG_NOTICE, "Device %s sent us multiple CC control frames. Ignoring those beyond the first.\n", device_name);
-			AST_LIST_UNLOCK(cc_interfaces->interface_tree);
-			return;
-		}
-	}
-	AST_LIST_UNLOCK(cc_interfaces->interface_tree);
-
-
-	if (!(tree_item = cc_device_tree_item_init(device_name, strlen(device_name), cc_interfaces->dial_parent_id, cc_data))) {
-		ast_log(LOG_WARNING, "Unable to create CC device interface for '%s'. CC services will be unavailable on this interface.\n", device_name);
-		return;
-	}
-
-	AST_LIST_LOCK(cc_interfaces->interface_tree);
-	AST_LIST_INSERT_TAIL(cc_interfaces->interface_tree, tree_item, next);
-	AST_LIST_UNLOCK(cc_interfaces->interface_tree);
-}
-
-static int dial_create_new_cc_core(struct ast_channel *inbound)
-{
-	struct ast_datastore *cc_datastore;
-	struct dial_cc_interfaces *cc_interfaces;
-	int is_tree_empty;
-
-	ast_channel_lock(inbound);
-	if (!(cc_datastore = ast_channel_datastore_find(inbound, &dial_cc_interfaces_info, NULL))) {
-		ast_channel_unlock(inbound);
-		return -1;
-	}
-	ast_channel_unlock(inbound);
-
-	cc_interfaces = cc_datastore->data;
-
-	if (cc_interfaces->nested_dial) {
-		/* Abort. Nested dials have no place trying
-		 * to create an instance of the core. Leave this
-		 * for the topmost Dial.
-		 */
-		return 0;
-	}
-
-	AST_LIST_LOCK(cc_interfaces->interface_tree);
-	is_tree_empty = AST_LIST_EMPTY(cc_interfaces->interface_tree);
-	AST_LIST_UNLOCK(cc_interfaces->interface_tree);
-
-	if (!is_tree_empty) {
-		ast_cc_core_init_instance(inbound, cc_interfaces->interface_tree);
-		ast_set_flag(inbound, AST_FLAG_OFFER_CC);
-	}
-	return 0;
-}
-
-/*!
- * \brief Create the root extension interface for this Dial attempt
- *
- * This function is called at the beginning of a dial attempt to get the
- * ball rolling as far as CC is concerned. See the in-line comments about the
- * three situations that can occur for more details.
- *
- * \param chan The channel on which the dial_cc_interfaces datastore is on or needs
- * to be created on
- * \param[out] ignore_cc If the datastore is already on the channel and the done flag
- * is set, then we will set this so that new CC offers will be ignored
- * \retval 0 Success
- * \retval -1 Failure
- */
-static int create_root_tree_item(struct ast_channel *chan, int *ignore_cc)
-{
-	/* There are three situations to deal with here:
-	 *
-	 * 1. The channel does not have a cc_interface datastore on
-	 * it. This means that this is the first time that Dial has
-	 * been called. We need to create/initialize the datastore.
-	 *
-	 * 2. The channel does have a cc_interface datastore on it and
-	 * the "done" indicator is 0. This means that a Local channel
-	 * was called by a "parent" dial. We can check the datastore's
-	 * parent field to see who the root of this particular dial tree
-	 * is.
-	 *
-	 * 3. The channel does have a cc_interface datastore on it and
-	 * the "done" indicator is 1. This means that a second Dial call
-	 * is being made from an extension. In this case, we do not
-	 * want to make any additions/modifications to the datastore. We
-	 * will instead set a flag to indicate that CCSS is completely
-	 * disabled for this Dial attempt.
-	 */
-
-	struct ast_datastore *cc_interfaces_datastore;
-	struct dial_cc_interfaces *interfaces;
-	struct ast_cc_tree_item *tree_item;
-	ast_channel_lock(chan);
-
-	if (ast_get_cc_agent_policy(ast_channel_get_cc_config_params(chan)) == AST_CC_AGENT_NEVER) {
-		/* We can't offer CC to this caller anyway, so don't bother with CC on this call
-		 */
-		*ignore_cc = 1;
-		ast_channel_unlock(chan);
-		return 0;
-	}
-
-	if (!(cc_interfaces_datastore = ast_channel_datastore_find(chan, &dial_cc_interfaces_info, NULL))) {
-		/* Situation 1 has occurred */
-		ast_channel_unlock(chan);
-		return cc_interfaces_datastore_init(chan);
-	}
-	ast_channel_unlock(chan);
-
-	interfaces = cc_interfaces_datastore->data;
-	if (interfaces->done) {
-		/* Situation 3 has occurred */
-		*ignore_cc = 1;
-		ast_log(LOG_NOTICE, "Datastore is present with done flag set. Ignoring CC offers on this call\n");
-		return 0;
-	}
-
-	/* Situation 2 has occurred */
-	if (!(tree_item = cc_extension_tree_item_init(S_OR(chan->macroexten, chan->exten), 
-			S_OR(chan->macrocontext, chan->context), interfaces->dial_parent_id))) {
-		return -1;
-	}
-	AST_LIST_LOCK(interfaces->interface_tree);
-	AST_LIST_INSERT_TAIL(interfaces->interface_tree, tree_item, next);
-	AST_LIST_UNLOCK(interfaces->interface_tree);
-	interfaces->dial_parent_id = tree_item->id;
-	return 0;
-}
-
 static void end_bridge_callback(void *data)
 {
 	char buf[80];
@@ -2129,7 +1658,7 @@
 		goto done;
 	}
 
-	if (create_root_tree_item(chan, &ignore_cc)) {
+	if (ast_cc_create_root_tree_item(chan, &ignore_cc)) {
 		goto done;
 	}
 
@@ -2855,7 +2384,7 @@
 	if (config.start_sound) {
 		ast_free((char *)config.start_sound);
 	}
-	cc_set_done_flag(chan);
+	ast_ignore_cc(chan);
 	return res;
 }
 
@@ -3006,7 +2535,6 @@
 	res = ast_register_application_xml(app, dial_exec);
 	res |= ast_register_application_xml(rapp, retrydial_exec);
 
-	dial_cc_interface_counter = 1;
 	return res;
 }
 

Modified: team/group/CCSS/include/asterisk/ccss.h
URL: http://svnview.digium.com/svn/asterisk/team/group/CCSS/include/asterisk/ccss.h?view=diff&rev=221659&r1=221658&r2=221659
==============================================================================
--- team/group/CCSS/include/asterisk/ccss.h (original)
+++ team/group/CCSS/include/asterisk/ccss.h Thu Oct  1 11:52:12 2009
@@ -346,6 +346,10 @@
 /* END CONFIGURATION FUNCTIONS */
 
 /* BEGIN TREE STRUCTURES AND APIS FOR APP_DIAL'S USE */
+void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data);
+void ast_ignore_cc(struct ast_channel *chan);
+int ast_cc_create_root_tree_item(struct ast_channel *chan, int *ignore_cc);
+int ast_cc_create_new_core(struct ast_channel *inbound);
 
 /*! Used to determine which type
  * of monitor an ast_cc_device_monitor

Modified: team/group/CCSS/main/ccss.c
URL: http://svnview.digium.com/svn/asterisk/team/group/CCSS/main/ccss.c?view=diff&rev=221659&r1=221658&r2=221659
==============================================================================
--- team/group/CCSS/main/ccss.c (original)
+++ team/group/CCSS/main/ccss.c Thu Oct  1 11:52:12 2009
@@ -477,6 +477,473 @@
 	}
 }
 
+/*!
+ * This counter is used for assigning unique ids
+ * to CC-enabled dialed interfaces.
+ */
+static int dial_cc_interface_counter;
+
+/*!
+ * \brief data stored in CC datastore
+ *
+ * The datastore creates a list of interfaces that were
+ * dialed, including both extensions and devices. In addition
+ * to the intrinsic data of the tree, some extra information
+ * is needed for use by app_dial.
+ */
+struct dial_cc_interfaces {
+	/*!
+	 * This value serves a dual-purpose. When dial starts, if the
+	 * dial_cc_interfaces datastore currently exists on the calling
+	 * channel, then the dial_parent_id will serve as a means of
+	 * letting the new extension ast_cc_tree_item we create know
+	 * who his parent is. This value will be the extension
+	 * ast_cc_tree_item that dialed the local channel that resulted
+	 * in the new Dial app being called.
+	 *
+	 * In addition, once an extension ast_cc_tree_item is created,
+	 * the dial_parent_id will be changed to the id of that newly
+	 * created interface. This way, device interfaces created from
+	 * receiving AST_CONTROL_CC frames can use this field to determine
+	 * who their parent extension interface should be.
+	 */
+	unsigned int dial_parent_id;
+	/*!
+	 * When a new Dial application is started, and the datastore
+	 * already exists on the channel, we can determine if we
+	 * should be adding any new interface information to tree.
+	 */
+	char done;
+	/*!
+	 * When nested dials occur (i.e. dialing local channels), we need
+	 * to be sure that they will not try to create a core instance themselves.
+	 * Instead, they need to pass the frame up to the parent dialing application.
+	 */
+	char nested_dial;
+	/*!
+	 * When nested dials occur (i.e. dialing local channels), the invocation
+	 * of the Local channel may have modifiers like /n or /b on the channel name.
+	 * This needs to be preserved as the dialable name on an extension tree item
+	 * which may be created for the local channel.
+	 */
+	char *current_extension_dialable_name;
+	/*!
+	 * Reference-counted "tree" of interfaces.
+	 */
+	struct ast_cc_interface_tree *interface_tree;
+};
+
+/*!
+ * \brief Destructor function for cc_interfaces datastore
+ *
+ * This function will free the actual datastore and drop
+ * the refcount for the interface tree by one. In cases
+ * where CC can actually be used, this unref will not
+ * result in the destruction of the interface tree, because
+ * the CC core will still have a reference.
+ *
+ * \param data The dial_cc_interfaces struct to destroy
+ */
+static void dial_cc_interfaces_destroy(void *data)
+{
+	struct dial_cc_interfaces *cc_interfaces = data;
+	ao2_t_ref(cc_interfaces->interface_tree, -1, "Unref dial's ref to interface tree");
+	ast_free(cc_interfaces);
+}
+
+/*!
+ * \brief Duplicate callback for cc_interfaces datastore
+ *
+ * Integers are copied by value, but the interface tree
+ * is done via a shallow copy and a bump of the refcount.
+ * This way, sub-Dials will be appending interfaces onto
+ * the same list as this call to Dial.
+ *
+ * \param data The old dial_cc_interfaces we want to copy
+ * \retval NULL Could not allocate memory for new dial_cc_interfaces
+ * \retval non-NULL The new copy of the dial_cc_interfaces
+ */
+static void *dial_cc_interfaces_duplicate(void *data)
+{
+	struct dial_cc_interfaces *old_cc_interfaces = data;
+	struct dial_cc_interfaces *new_cc_interfaces = ast_calloc(1, sizeof(*new_cc_interfaces));
+	if (!new_cc_interfaces) {
+		return NULL;
+	}
+	new_cc_interfaces->done = old_cc_interfaces->done;
+	new_cc_interfaces->dial_parent_id = old_cc_interfaces->dial_parent_id;
+	new_cc_interfaces->nested_dial = 1;
+	ao2_t_ref(old_cc_interfaces->interface_tree, +1, "New ref due to duplication of interface tree");
+	new_cc_interfaces->interface_tree = old_cc_interfaces->interface_tree;
+	return new_cc_interfaces;
+}
+
+/*!
+ * \brief information regarding the dial_cc_interfaces datastore
+ *
+ * The dial_cc_interfaces datastore is responsible for keeping track
+ * of what CC-enabled interfaces have been dialed by the caller. For
+ * more information regarding the actual structure of the tree, see
+ * the documentation provided in include/asterisk/ccss.h
+ */
+static const struct ast_datastore_info dial_cc_interfaces_info = {
+	.type = "Dial CC Interfaces",
+	.duplicate = dial_cc_interfaces_duplicate,
+	.destroy = dial_cc_interfaces_destroy,
+};
+
+/*!
+ * \brief Routine to set the done flag on the dial_cc_interfaces datastore
+ *
+ * When we determine that further calls to Dial in a particular thread should
+ * not pay attention to any new CC offers, we set this flag as an indicator.
+ * 
+ * \param chan The calling channel with teh dial_cc_interfaaces datastore on it
+ * \return void
+ */
+void ast_ignore_cc(struct ast_channel *chan)
+{
+	struct ast_datastore *cc_datastore;
+	struct dial_cc_interfaces *cc_interfaces;
+
+	ast_channel_lock(chan);
+	if ((cc_datastore = ast_channel_datastore_find(chan, &dial_cc_interfaces_info, NULL))) {
+		cc_interfaces = cc_datastore->data;
+		cc_interfaces->done = 1;
+	}
+	ast_channel_unlock(chan);
+}
+
+/*!
+ * \brief Allocate and initialize an "extension" interface for CC purposes
+ *
+ * When app_dial starts, this function is called in order to set up the
+ * information about the extension in which this Dial is occurring. Any
+ * devices dialed will have this particular ast_cc_tree_item as a parent.
+ *
+ * \param exten Extension from which Dial is occurring
+ * \param context Context to which exten belongs
+ * \param parent_id What should we set the parent_id of this interface to?
+ * \retval NULL Memory allocation failure
+ * \retval non-NULL The newly-created ast_cc_tree_item for the extension
+ */
+static struct ast_cc_tree_item *cc_extension_tree_item_init(const char * const exten, const char * const context, const unsigned int parent_id)
+{
+	struct ast_str *str = ast_str_alloca(2 * AST_MAX_EXTENSION);
+	struct ast_cc_interface *cc_interface;
+	struct ast_cc_tree_item *tree_item;
+
+	ast_str_set(&str, 0, "%s@%s", exten, context);
+
+	if (!(cc_interface = ao2_alloc(sizeof(*cc_interface) + ast_str_strlen(str), ast_cc_interface_destroy))) {
+		return NULL;
+	}
+
+	if (!(tree_item = ast_calloc(1, sizeof(*tree_item)))) {
+		ao2_t_ref(cc_interface, -1, "failed to allocate the tree item, so unref the interface");
+		return NULL;
+	}
+
+	tree_item->id = ast_atomic_fetchadd_int(&dial_cc_interface_counter, +1);
+	tree_item->parent_id = parent_id;
+	cc_interface->monitor_type = "extension";
+	cc_interface->monitor_class = AST_CC_EXTENSION_MONITOR;
+	strcpy(cc_interface->name, ast_str_buffer(str));
+	tree_item->interface = cc_interface;
+	ast_log(LOG_NOTICE, "Created an extension cc interface for '%s' with id %d and parent %d\n", cc_interface->name, tree_item->id, tree_item->parent_id);
+	return tree_item;
+}
+
+/*!
+ * \brief allocate dial_cc_interfaces datastore and initialize fields
+ *
+ * This function is called when Situation 1 occurs in create_root_tree_item.
+ * See that function for more information on what Situation 1 is.
+ *
+ * In this particular case, we have to do a lot of memory allocation in order
+ * to create the datastore, the data for the datastore, the tree of interfaces
+ * that we'll be adding to, and the initial extension interface for this Dial
+ * attempt.
+ *
+ * \param chan The channel onto which the datastore should be added.
+ * \retval -1 An error occurred
+ * \retval 0 Success
+ */
+static int cc_interfaces_datastore_init(struct ast_channel *chan) {
+	struct dial_cc_interfaces *interfaces;
+	struct ast_cc_tree_item *tree_item;
+	struct ast_datastore *dial_cc_datastore;
+
+	if (!(interfaces = ast_calloc(1, sizeof(*interfaces)))) {
+		return -1;
+	}
+
+	if (!(tree_item = cc_extension_tree_item_init(S_OR(chan->macroexten, chan->exten), S_OR(chan->macrocontext, chan->context), 0))) {
+		ast_free(interfaces);
+		return -1;
+	}
+
+	if (!(dial_cc_datastore = ast_datastore_alloc(&dial_cc_interfaces_info, NULL))) {
+		ast_cc_tree_item_destroy(tree_item);
+		ast_free(interfaces);
+		return -1;
+	}
+
+	if (!(interfaces->interface_tree = ao2_t_alloc(sizeof(*interfaces->interface_tree), ast_cc_interface_tree_destroy,
+					"Allocate interface tree"))) {
+		ast_datastore_free(dial_cc_datastore);
+		ast_cc_tree_item_destroy(tree_item);
+		ast_free(interfaces);
+		return -1;
+	}
+
+	/* Finally, all that allocation is done... */
+	AST_LIST_HEAD_INIT(interfaces->interface_tree);
+	AST_LIST_INSERT_TAIL(interfaces->interface_tree, tree_item, next);
+	dial_cc_datastore->data = interfaces;
+	dial_cc_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+	interfaces->dial_parent_id = tree_item->id;
+	ast_channel_lock(chan);
+	ast_channel_datastore_add(chan, dial_cc_datastore);
+	ast_channel_unlock(chan);
+	return 0;
+}
+
+/*!
+ * \brief Allocate and intitialize a device ast_cc_tree_item
+ *
+ * For all intents and purposes, this is the same as
+ * cc_extension_tree_item_init, except that there is only
+ * a single parameter used for naming the interface.
+ *
+ * This function is called when handling AST_CONTROL_CC frames.
+ * The device has reported that CC is possible, so we add it
+ * to the interface_tree.
+ *
+ * Note that it is not necessarily erroneous to add the same
+ * device to the tree twice. If the same device is called by
+ * two different extension during the same call, then
+ * that is a legitimate situation. Of course, I'm pretty sure
+ * the dialed_interfaces global datastore will not allow that
+ * to happen anyway.
+ *
+ * \param name The name of the device being added to the tree
+ * \param name_len The length of name, in bytes
+ * \param parent_id The parent of this new tree node.
+ * \retval NULL Memory allocation failure
+ * \retval non-NULL The new ast_cc_interface created.
+ */
+static struct ast_cc_tree_item *cc_device_tree_item_init(const char * const name, const size_t name_len,
+		const int parent_id, const struct ast_control_cc_payload *cc_data)
+{
+	struct ast_cc_interface *cc_interface = ao2_alloc(sizeof(*cc_interface) + name_len, ast_cc_interface_destroy);
+	struct ast_cc_tree_item *tree_item;
+
+	if (!cc_interface) {
+		return NULL;
+	}
+
+	if (!(cc_interface->config_params = ast_cc_config_params_init())) {
+		ao2_t_ref(cc_interface, -1, "Failed to allocate config params, unref interface");
+		return NULL;
+	}
+
+	if (!(tree_item = ast_calloc(1, sizeof(*tree_item)))) {
+		ao2_t_ref(cc_interface, -1, "Failed to allocate tree item, unref interface");
+		return NULL;
+	}
+
+	strcpy(cc_interface->name, name);
+	tree_item->id = ast_atomic_fetchadd_int(&dial_cc_interface_counter, +1);
+	tree_item->parent_id = parent_id;
+	tree_item->service_offered = cc_data->service;
+	cc_interface->monitor_type = cc_data->monitor_type;
+	cc_interface->monitor_class = AST_CC_DEVICE_MONITOR;
+	tree_item->interface = cc_interface;
+	ast_cc_copy_config_params(cc_interface->config_params, cc_data->config_params);
+	ast_log(LOG_NOTICE, "Created a device cc interface for '%s' with id %d and parent %d\n", cc_interface->name, tree_item->id, tree_item->parent_id);
+	return tree_item;
+}
+
+/*!
+ * \brief Function for handling AST_CONTROL_CC frames
+ *
+ * Unless we are ignoring CC for some reason, we will always
+ * call this function when we read an AST_CONTROL_CC frame
+ * from an outbound channel.
+ *
+ * This function will call cc_device_tree_item_init to
+ * create the new ast_cc_tree_item for the device from which
+ * we read the frame. In addition, the new device will be added
+ * to the interface tree on the dial_cc_interfaces datastore
+ * on the inbound channel.
+ *
+ * If this is the first AST_CONTROL_CC frame that we have handled
+ * for this call, then we will also initialize the CC core for
+ * this call.
+ *
+ * \param inbound The caller's channel
+ * \param outbound The channel we read the control frame from
+ * \param frame_data The data on the frame (XXX currently unused)
+ * \return void
+ */
+void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data)
+{
+	char device_name[AST_CHANNEL_NAME];
+	char *dash;
+	struct ast_cc_tree_item *tree_item;
+	struct ast_datastore *cc_datastore;
+	struct dial_cc_interfaces *cc_interfaces;
+	struct ast_control_cc_payload *cc_data = frame_data;
+
+	ast_copy_string(device_name, outbound->name, sizeof(device_name));
+	if ((dash = strrchr(device_name, '-'))) {
+		*dash = '\0';
+	}
+
+	if (!(cc_datastore = ast_channel_datastore_find(inbound, &dial_cc_interfaces_info, NULL))) {
+		ast_log(LOG_WARNING, "Unable to retrieve CC datastore while processing CC frame from '%s'. CC services will be unavailable.\n", device_name);
+		return;
+	}
+
+	cc_interfaces = cc_datastore->data;
+
+	/* Yeah this kind of sucks, but luckily most people
+	 * aren't dialing thousands of interfaces on every call
+	 *
+	 * This traversal helps us to not create duplicate tree items in
+	 * case a device queues multiple CC control frames.
+	 */
+	AST_LIST_LOCK(cc_interfaces->interface_tree);
+	AST_LIST_TRAVERSE(cc_interfaces->interface_tree, tree_item, next) {
+		if (!strcmp(tree_item->interface->name, device_name)) {
+			ast_log(LOG_NOTICE, "Device %s sent us multiple CC control frames. Ignoring those beyond the first.\n", device_name);
+			AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+			return;
+		}
+	}
+	AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+
+
+	if (!(tree_item = cc_device_tree_item_init(device_name, strlen(device_name), cc_interfaces->dial_parent_id, cc_data))) {
+		ast_log(LOG_WARNING, "Unable to create CC device interface for '%s'. CC services will be unavailable on this interface.\n", device_name);
+		return;
+	}
+
+	AST_LIST_LOCK(cc_interfaces->interface_tree);
+	AST_LIST_INSERT_TAIL(cc_interfaces->interface_tree, tree_item, next);
+	AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+}
+
+int ast_cc_create_new_core(struct ast_channel *inbound)
+{
+	struct ast_datastore *cc_datastore;
+	struct dial_cc_interfaces *cc_interfaces;
+	int is_tree_empty;
+
+	ast_channel_lock(inbound);
+	if (!(cc_datastore = ast_channel_datastore_find(inbound, &dial_cc_interfaces_info, NULL))) {
+		ast_channel_unlock(inbound);
+		return -1;
+	}
+	ast_channel_unlock(inbound);
+
+	cc_interfaces = cc_datastore->data;
+
+	if (cc_interfaces->nested_dial) {
+		/* Abort. Nested dials have no place trying
+		 * to create an instance of the core. Leave this
+		 * for the topmost Dial.
+		 */
+		return 0;
+	}
+
+	AST_LIST_LOCK(cc_interfaces->interface_tree);
+	is_tree_empty = AST_LIST_EMPTY(cc_interfaces->interface_tree);
+	AST_LIST_UNLOCK(cc_interfaces->interface_tree);
+
+	if (!is_tree_empty) {
+		ast_cc_core_init_instance(inbound, cc_interfaces->interface_tree);
+		ast_set_flag(inbound, AST_FLAG_OFFER_CC);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Create the root extension interface for this Dial attempt
+ *
+ * This function is called at the beginning of a dial attempt to get the
+ * ball rolling as far as CC is concerned. See the in-line comments about the
+ * three situations that can occur for more details.
+ *
+ * \param chan The channel on which the dial_cc_interfaces datastore is on or needs
+ * to be created on
+ * \param[out] ignore_cc If the datastore is already on the channel and the done flag
+ * is set, then we will set this so that new CC offers will be ignored
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_cc_create_root_tree_item(struct ast_channel *chan, int *ignore_cc)
+{
+	/* There are three situations to deal with here:
+	 *
+	 * 1. The channel does not have a cc_interface datastore on
+	 * it. This means that this is the first time that Dial has
+	 * been called. We need to create/initialize the datastore.
+	 *
+	 * 2. The channel does have a cc_interface datastore on it and
+	 * the "done" indicator is 0. This means that a Local channel
+	 * was called by a "parent" dial. We can check the datastore's
+	 * parent field to see who the root of this particular dial tree
+	 * is.
+	 *
+	 * 3. The channel does have a cc_interface datastore on it and
+	 * the "done" indicator is 1. This means that a second Dial call

[... 56 lines stripped ...]



More information about the svn-commits mailing list