Index: funcs/func_volume.c
===================================================================
--- funcs/func_volume.c	(revision 228826)
+++ funcs/func_volume.c	(working copy)
@@ -36,63 +36,31 @@
 #include "asterisk/utils.h"
 #include "asterisk/audiohook.h"
 
-struct volume_information {
-	struct ast_audiohook audiohook;
-	int tx_gain;
-	int rx_gain;
-};
-
-static void destroy_callback(void *data)
+static int volume_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
-	struct volume_information *vi = data;
+	char *ret = NULL;
 
-	/* Destroy the audiohook, and destroy ourselves */
-	ast_audiohook_destroy(&vi->audiohook);
-	free(vi);
+	*buf = '\0';
 
-	return;
-}
+	if (!strcasecmp(data, "tx")) {
+		sprintf(ret, "%d", ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE));
+	} else if (!strcasecmp(data, "rx")) {
+		sprintf(ret, "%d", ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ));
+	} else if (!strcasecmp(data, "rx_up")) {
+		sprintf(ret, "%s", (char *) ast_audiohook_volume_get_full(chan, AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_VOLUME_GET_KEY_UP));
+	} else if (!strcasecmp(data, "rx_down")) {
+		sprintf(ret, "%s", (char *) ast_audiohook_volume_get_full(chan, AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_VOLUME_GET_KEY_DOWN));
+	} else if (!strcasecmp(data, "tx_up")) {
+		sprintf(ret, "%s", (char *) ast_audiohook_volume_get_full(chan, AST_AUDIOHOOK_DIRECTION_WRITE, AST_AUDIOHOOK_VOLUME_GET_KEY_UP));
+	} else if (!strcasecmp(data, "tx_down")) {
+		sprintf(ret, "%s", (char *) ast_audiohook_volume_get_full(chan, AST_AUDIOHOOK_DIRECTION_WRITE, AST_AUDIOHOOK_VOLUME_GET_KEY_DOWN));
+	} else {
+		ast_log(LOG_ERROR, "Unknown volume data type '%s'.\n", data);
+		return -1;
+	}
 
-/*! \brief Static structure for datastore information */
-static const struct ast_datastore_info volume_datastore = {
-	.type = "volume",
-	.destroy = destroy_callback
-};
-
-static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
-{
-	struct ast_datastore *datastore = NULL;
-	struct volume_information *vi = NULL;
-	int *gain = NULL;
-
-	/* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
-	if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
-		return 0;
-
-	/* Grab datastore which contains our gain information */
-	if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL)))
-		return 0;
-
-	vi = datastore->data;
-
-	/* If this is DTMF then allow them to increase/decrease the gains */
-	if (frame->frametype == AST_FRAME_DTMF) {
-		/* Only use DTMF coming from the source... not going to it */
-		if (direction != AST_AUDIOHOOK_DIRECTION_READ)
-			return 0;
-		if (frame->subclass == '*') {
-			vi->tx_gain += 1;
-			vi->rx_gain += 1;
-		} else if (frame->subclass == '#') {
-			vi->tx_gain -= 1;
-			vi->rx_gain -= 1;
-		}
-	} else if (frame->frametype == AST_FRAME_VOICE) {
-		/* Based on direction of frame grab the gain, and confirm it is applicable */
-		if (!(gain = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? &vi->rx_gain : &vi->tx_gain) || !*gain)
-			return 0;
-		/* Apply gain to frame... easy as pi */
-		ast_frame_adjust_volume(frame, *gain);
+	if (ret) {
+		ast_copy_string(buf, ret, len);
 	}
 
 	return 0;
@@ -100,50 +68,38 @@
 
 static int volume_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
 {
-	struct ast_datastore *datastore = NULL;
-	struct volume_information *vi = NULL;
-	int is_new = 0;
-
-	if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL))) {
-		/* Allocate a new datastore to hold the reference to this volume and audiohook information */
-		if (!(datastore = ast_datastore_alloc(&volume_datastore, NULL)))
-			return 0;
-		if (!(vi = ast_calloc(1, sizeof(*vi)))) {
-			ast_datastore_free(datastore);
-			return 0;
-		}
-		ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume");
-		vi->audiohook.manipulate_callback = volume_callback;
-		ast_set_flag(&vi->audiohook, AST_AUDIOHOOK_WANTS_DTMF);
-		is_new = 1;
+	if (!strcasecmp(data, "tx")) {
+		ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, atoi(value));
+	} else if (!strcasecmp(data, "rx")) {
+		ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, atoi(value));
+	} else if (!strcasecmp(data, "rx_up")) {
+		ast_audiohook_volume_full(chan, AST_AUDIOHOOK_DIRECTION_READ, 0, AST_AUDIOHOOK_VOLUME_ADJUST, value[0], 0, 0, 0);
+	} else if (!strcasecmp(data, "tx_up")) {
+		ast_audiohook_volume_full(chan, AST_AUDIOHOOK_DIRECTION_READ, 0, AST_AUDIOHOOK_VOLUME_ADJUST, 0, value[0], 0, 0);
+	} else if (!strcasecmp(data, "rx_down")) {
+		ast_audiohook_volume_full(chan, AST_AUDIOHOOK_DIRECTION_READ, 0, AST_AUDIOHOOK_VOLUME_ADJUST, 0, 0, value[0], 0);
+	} else if (!strcasecmp(data, "tx_down")) {
+		ast_audiohook_volume_full(chan, AST_AUDIOHOOK_DIRECTION_READ, 0, AST_AUDIOHOOK_VOLUME_ADJUST, 0, 0, 0, value[0]);
 	} else {
-		vi = datastore->data;
+		ast_log(LOG_ERROR, "Unknown volume data type '%s'.\n", data);
+		return -1;
 	}
 
-	/* Adjust gain on volume information structure */
-	if (!strcasecmp(data, "tx"))
-		vi->tx_gain = atoi(value);
-	else if (!strcasecmp(data, "rx"))
-		vi->rx_gain = atoi(value);
-
-	if (is_new) {
-		datastore->data = vi;
-		ast_channel_datastore_add(chan, datastore);
-		ast_audiohook_attach(chan, &vi->audiohook);
-	}
-
 	return 0;
 }
 
 static struct ast_custom_function volume_function = {
 	.name = "VOLUME",
-	.synopsis = "Set the TX or RX volume of a channel",
-	.syntax = "VOLUME(TX|RX)",
+	.synopsis = "Set the TX or RX volume of a channel and optionaly assign DTMF keys to update them during a call",
+	.syntax = "VOLUME(TX|RX|TX_UP|RX_UP|TX_DOWN|RX_DOWN)",
 	.desc =
 	"  The VOLUME function can be used to increase or decrease the tx or\n"
 	"rx gain of any channel.  For example:\n"
 	"  Set(VOLUME(TX)=3)\n"
-	"  Set(VOLUME(RX)=2)\n",
+	"  Set(VOLUME(RX)=$[${VOLUME(RX)} + 2])\n",
+	"  Set(VOLUME(TX_UP)=*)\n"
+	"  Set(VOLUME(TX_DOWN)=#)\n",
+	.read = volume_read,
 	.write = volume_write,
 };
 
Index: include/asterisk/audiohook.h
===================================================================
--- include/asterisk/audiohook.h	(revision 228826)
+++ include/asterisk/audiohook.h	(working copy)
@@ -64,6 +64,17 @@
 	AST_AUDIOHOOK_SMALL_QUEUE = (1 << 3),
 };
 
+enum ast_audiohook_volume_do {
+	AST_AUDIOHOOK_VOLUME_SET = 0,    /*!< Audiohook should (re)set the volume */
+	AST_AUDIOHOOK_VOLUME_ADJUST,     /*!< Audiohook adjust the volume */
+};
+
+enum ast_audiohook_volume_arg {
+	AST_AUDIOHOOK_VOLUME_GET_VOLUME = 0,    /*!< Get the write volume */
+	AST_AUDIOHOOK_VOLUME_GET_KEY_UP,        /*!< Get the key assigned for the volume increase */
+	AST_AUDIOHOOK_VOLUME_GET_KEY_DOWN,      /*!< Get the key assigned for the volume decrease  */
+};
+
 #define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*< Tolerance in milliseconds for audiohooks synchronization */
 
 struct ast_audiohook;
@@ -239,35 +250,63 @@
  */
 #define ast_audiohook_unlock(ah) ast_mutex_unlock(&(ah)->lock)
 
-/*!
- * \brief Adjust the volume on frames read from or written to a channel
+/*! \brief Set or adjust the volume on frames read from or written to a channel and optionaly set a digit(s) to update them
  * \param chan Channel to muck with
  * \param direction Direction to set on
- * \param volume Value to adjust the volume by
+ * \param volume Value to set to or adjust the volume by
+ * \param adjust Adjust volume instead of set
+ * \param read_up DTMF digit to increase read volume. If 0 - unchanged and -1 to remove the asignment
+ * \param write_up DTMF digit to increase write volume. If 0 - unchanged and -1 to remove the asignment
+ * \param read_down DTMF digit to decrease read volume. If 0 - unchanged and -1 to remove the asignment
+ * \param write_down DTMF digit to decrease write volume. If 0 - unchanged and -1 to remove the asignment
  * \return Returns 0 on success, -1 on failure
  * \since 1.6.1
  */
-int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume);
+int ast_audiohook_volume_full(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume, enum ast_audiohook_volume_do adjust, int read_up, int write_up, int read_down, int write_down);
 
-/*!
- * \brief Retrieve the volume adjustment value on frames read from or written to a channel
- * \param chan Channel to retrieve volume adjustment from
- * \param direction Direction to retrieve
- * \return Returns adjustment value
+/*! \brief Set the volume on frames read from or written to a channel
+ * \param chan Channel to muck with
+ * \param direction Direction to set on
+ * \param volume Value to set the volume to
+ * \return Returns 0 on success, -1 on failure
  * \since 1.6.1
  */
-int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction);
+static inline int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
+{
+	return ast_audiohook_volume_full(chan, direction, volume, AST_AUDIOHOOK_VOLUME_SET, 0, 0, 0, 0);
+}
 
 /*!
  * \brief Adjust the volume on frames read from or written to a channel
  * \param chan Channel to muck with
- * \param direction Direction to increase
+ * \param direction Direction to adjust
  * \param volume Value to adjust the adjustment by
  * \return Returns 0 on success, -1 on failure
  * \since 1.6.1
  */
-int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume);
+static inline int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
+{
+	return ast_audiohook_volume_full(chan, direction, volume, AST_AUDIOHOOK_VOLUME_ADJUST, 0, 0, 0, 0);
+}
 
+/*! \brief Retrieve the volume adjustment value on frames read from or written to a channel
+ * \param chan Channel to retrieve volume adjustment from
+ * \param direction Direction to retrieve
+ * \param to_get What to retrieve - volume, Up or Down key
+ * \return Returns adjustment value
+ */
+int ast_audiohook_volume_get_full(struct ast_channel *chan, enum ast_audiohook_direction direction, enum ast_audiohook_volume_arg to_get);
+
+/*! \brief Retrieve the volume adjustment value on frames read from or written to a channel
+ * \param chan Channel to retrieve volume adjustment from
+ * \param direction Direction to retrieve
+ * \return Returns adjustment value
+ */
+static inline int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
+{
+	return ast_audiohook_volume_get_full(chan, direction, AST_AUDIOHOOK_VOLUME_GET_VOLUME);
+}
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
Index: main/audiohook.c
===================================================================
--- main/audiohook.c	(revision 228826)
+++ main/audiohook.c	(working copy)
@@ -810,6 +810,10 @@
 	struct ast_audiohook audiohook; /*!< Audiohook attached to the channel */
 	int read_adjustment;            /*!< Value to adjust frames read from the channel by */
 	int write_adjustment;           /*!< Value to adjust frames written to the channel by */
+	int read_up;                    /*!< DTMF to increase read volume */
+	int write_up;                   /*!< DTMF to increase write volume */
+	int read_down;                  /*!< DTMF to decrease read volume */
+	int write_down;                 /*!< DTMF to decrease write volume */
 };
 
 /*! \brief Callback used to destroy the audiohook volume datastore
@@ -848,8 +852,8 @@
 	struct audiohook_volume *audiohook_volume = NULL;
 	int *gain = NULL;
 
-	/* If the audiohook is shutting down don't even bother */
-	if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {
+	/* If the audiohook is shutting down or we do not have a frame don't even bother */
+	if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || !frame) {
 		return 0;
 	}
 
@@ -860,6 +864,27 @@
 
 	audiohook_volume = datastore->data;
 
+	/* If this is DTMF then allow them to increase/decrease the gains */
+	if (frame->frametype == AST_FRAME_DTMF) {
+		/* Only use DTMF coming from the source... not going to it */
+		if (direction != AST_AUDIOHOOK_DIRECTION_READ ) {
+			return 0;
+		}
+
+		if (audiohook_volume->read_up && frame->subclass == audiohook_volume->read_up) {
+			audiohook_volume->read_adjustment += 1;
+		} else if (audiohook_volume->read_down && frame->subclass == audiohook_volume->read_down) {
+			audiohook_volume->read_adjustment -= 1;
+		}
+		/* We may have the same DTMF assigned for both read and write so check them separately */
+		if (audiohook_volume->write_up && frame->subclass == audiohook_volume->write_up) {
+			audiohook_volume->write_adjustment += 1;
+		} else if (audiohook_volume->write_down && frame->subclass == audiohook_volume->write_down) {
+			audiohook_volume->write_adjustment -= 1;
+		}
+		return 0;
+	}
+
 	/* Based on direction grab the appropriate adjustment value */
 	if (direction == AST_AUDIOHOOK_DIRECTION_READ) {
 		gain = &audiohook_volume->read_adjustment;
@@ -918,10 +943,15 @@
 /*! \brief Adjust the volume on frames read from or written to a channel
  * \param chan Channel to muck with
  * \param direction Direction to set on
- * \param volume Value to adjust the volume by
- * \return Returns 0 on success, -1 on failure
+ * \param volume Value to set the volume to
+ * \param adjust Adjust volume instead of set
+ * \param read_up DTMF digit to increase read volume
+ * \param write_up DTMF digit to increase write volume
+ * \param read_down DTMF digit to decrease read volume
+ * \param write_down DTMF digit to decrease write volume
+* \return Returns 0 on success, -1 on failure
  */
-int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
+int ast_audiohook_volume_full(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume, enum ast_audiohook_volume_do adjust, int read_up, int write_up, int read_down, int write_down)
 {
 	struct audiohook_volume *audiohook_volume = NULL;
 
@@ -932,24 +962,45 @@
 
 	/* Now based on the direction set the proper value */
 	if (direction == AST_AUDIOHOOK_DIRECTION_READ || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
-		audiohook_volume->read_adjustment = volume;
+		audiohook_volume->read_adjustment = (adjust == AST_AUDIOHOOK_VOLUME_SET) ? volume : audiohook_volume->read_adjustment + volume;
 	}
 	if (direction == AST_AUDIOHOOK_DIRECTION_WRITE || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
-		audiohook_volume->write_adjustment = volume;
+		audiohook_volume->write_adjustment = (adjust == AST_AUDIOHOOK_VOLUME_SET) ? volume : audiohook_volume->write_adjustment + volume;
 	}
 
+	/* If DTMF key set or change requested - do it */
+	if (read_up) {
+		audiohook_volume->read_up = (read_up > 0) ? read_up : 0;
+	}
+	if (write_up) {
+		audiohook_volume->write_up = (write_up > 0) ? write_up : 0;
+	}
+	if (read_down) {
+		audiohook_volume->read_down = (read_down > 0) ? read_down : 0;
+	}
+	if (write_down) {
+		audiohook_volume->write_down = (write_down > 0) ? write_down : 0;
+	}
+
+	if (audiohook_volume->read_up || audiohook_volume->write_up || audiohook_volume->read_down || audiohook_volume->write_down) {
+		ast_set_flag(&audiohook_volume->audiohook, AST_AUDIOHOOK_WANTS_DTMF);
+	} else {
+		ast_clear_flag(&audiohook_volume->audiohook, AST_AUDIOHOOK_WANTS_DTMF);
+	}
+
 	return 0;
 }
 
 /*! \brief Retrieve the volume adjustment value on frames read from or written to a channel
  * \param chan Channel to retrieve volume adjustment from
  * \param direction Direction to retrieve
+ * \param to_get What to retrieve - volume, Up or Down key
  * \return Returns adjustment value
  */
-int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
+int ast_audiohook_volume_get_full(struct ast_channel *chan, enum ast_audiohook_direction direction, enum ast_audiohook_volume_arg to_get)
 {
 	struct audiohook_volume *audiohook_volume = NULL;
-	int adjustment = 0;
+	int retval = 0;
 
 	/* Attempt to find the audiohook volume information, but do not create it as we only want to look at the values */
 	if (!(audiohook_volume = audiohook_volume_get(chan, 0))) {
@@ -958,36 +1009,22 @@
 
 	/* Grab the adjustment value based on direction given */
 	if (direction == AST_AUDIOHOOK_DIRECTION_READ) {
-		adjustment = audiohook_volume->read_adjustment;
+		if (to_get == AST_AUDIOHOOK_VOLUME_GET_VOLUME) {
+			retval = audiohook_volume->read_adjustment;
+		} else if (to_get == AST_AUDIOHOOK_VOLUME_GET_KEY_UP) {
+			retval = audiohook_volume->read_up;
+		} else if (to_get == AST_AUDIOHOOK_VOLUME_GET_KEY_DOWN) {
+			retval = audiohook_volume->read_down;
+		}
 	} else if (direction == AST_AUDIOHOOK_DIRECTION_WRITE) {
-		adjustment = audiohook_volume->write_adjustment;
+		if (to_get == AST_AUDIOHOOK_VOLUME_GET_VOLUME) {
+			retval = audiohook_volume->write_adjustment;
+		} else if (to_get == AST_AUDIOHOOK_VOLUME_GET_KEY_UP) {
+			retval = audiohook_volume->write_up;
+		} else if (to_get == AST_AUDIOHOOK_VOLUME_GET_KEY_DOWN) {
+			retval = audiohook_volume->write_down;
+		}
 	}
 
-	return adjustment;
+	return retval;
 }
-
-/*! \brief Adjust the volume on frames read from or written to a channel
- * \param chan Channel to muck with
- * \param direction Direction to increase
- * \param volume Value to adjust the adjustment by
- * \return Returns 0 on success, -1 on failure
- */
-int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
-{
-	struct audiohook_volume *audiohook_volume = NULL;
-
-	/* Attempt to find the audiohook volume information, and create an audiohook if none exists */
-	if (!(audiohook_volume = audiohook_volume_get(chan, 1))) {
-		return -1;
-	}
-
-	/* Based on the direction change the specific adjustment value */
-	if (direction == AST_AUDIOHOOK_DIRECTION_READ || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
-		audiohook_volume->read_adjustment += volume;
-	}
-	if (direction == AST_AUDIOHOOK_DIRECTION_WRITE || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
-		audiohook_volume->write_adjustment += volume;
-	}
-
-	return 0;
-}
