diff --git a/CREDITS b/CREDITS
index 6c8f7ff1665f64d1a1f2bb1ee74eed232817ecfe..a9e968799a29fbab1b171865119d30a84b0d3d6c 100644
--- a/CREDITS
+++ b/CREDITS
@@ -222,6 +222,8 @@ ClearIT AB, Sweden - res_mutestream, queue_exists and various other patches (dev
 Despegar.com, Argentina - AstData API implementation, also sponsored by Google as part of the
 	gsoc/2009 program (developed by Eliel)
 
+Philippe Lindheimer - DEV_STATE additions to CCSS
+
 === OTHER CONTRIBUTIONS ===
 John Todd - Monkey sounds and associated teletorture prompt
 Michael Jerris - bug marshaling
diff --git a/configs/ccss.conf.sample b/configs/ccss.conf.sample
index bb78cad0c64f2c1890231766a9060598fee38f89..2636f7ec9ec81db7f7a296f90fcf3c8824168d41 100644
--- a/configs/ccss.conf.sample
+++ b/configs/ccss.conf.sample
@@ -6,12 +6,54 @@
 ;
 
 [general]
-; There is only a single option that may be defined in this file.
 ; The cc_max_requests option is a global limit on the number of
 ; CC requests that may be in the Asterisk system at any time.
 ;
 ;cc_max_requests = 20
 ;
+; The cc_STATE_devstate variables listed below can be used to change the
+; default mapping of the internal state machine tracking the state of
+; call completion to an Asterisk Device State value. The acceptable values
+; that can be provided are as follows, with a description of what the
+; equivalent device BLF that this maps to:
+;
+;	UNKNOWN      ; Device is valid but channel didn't know state
+;	NOT_INUSE    ; Device is not used
+;	INUSE        ; Device is in use
+;	BUSY         ; Device is busy
+;	INVALID      ; Device is invalid
+;	UNAVAILABLE  ; Device is unavailable
+;	RINGING      ; Device is ringing
+;	RINGINUSE    ; Device is ringing *and* in use
+;	ONHOLD       ; Device is on hold
+;
+; These states are used to generate DEVICE_STATE information that can be
+; included with Asterisk hints for phones to subscribe to the state information
+; or dialplan to check the state using the EXTENSION_STATE() function or
+; the DEVICE_STATE() function.
+;
+; The states are in the format of: "ccss:TECH/ID" so an example of device
+; SIP/3000 making a CallCompletionRequest() could be checked  by looking at
+; DEVICE_STATE(ccss:SIP/3000) or an Asterisk Hint could be generated such as
+;
+; [hint-context]
+; exten => *843000,hint,ccss:SIP/3000
+;
+; and then accessed with EXTENSION_STATE(*843000@hint-context)
+; or subscribed to with a BLF button on a phone.
+;
+; The available state mapping and default values are:
+;
+; cc_available_devstate = NOT_INUSE
+; cc_offered_devstate = NOT_INUSE
+; cc_caller_requested_devstate = NOT_INUSE
+; cc_active_devstate = INUSE
+; cc_callee_ready_devstate = INUSE
+; cc_caller_busy_devstate = ONHOLD
+; cc_recalling_devstate = RINGING
+; cc_complete_devstate = NOT_INUSE
+; cc_failed_devstate = NOT_INUSE
+
 ;
 ;============================================
 ;           PLEASE READ THIS!!!
diff --git a/main/ccss.c b/main/ccss.c
index cdd0be7a9325e784273fbc35768342d11bdc944d..e440b340e624e1ce8fa9ced0f371edb140c6bb45 100644
--- a/main/ccss.c
+++ b/main/ccss.c
@@ -33,6 +33,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/taskprocessor.h"
 #include "asterisk/event.h"
+#include "asterisk/devicestate.h"
 #include "asterisk/module.h"
 #include "asterisk/app.h"
 #include "asterisk/cli.h"
@@ -530,6 +531,111 @@ static int count_agents_cb(void *obj, void *arg, void *data, int flags)
 	return 0;
 }
 
+/* default values mapping from cc_state to ast_dev_state */
+
+#define CC_AVAILABLE_DEVSTATE_DEFAULT        AST_DEVICE_NOT_INUSE
+#define CC_CALLER_OFFERED_DEVSTATE_DEFAULT   AST_DEVICE_NOT_INUSE
+#define CC_CALLER_REQUESTED_DEVSTATE_DEFAULT AST_DEVICE_NOT_INUSE
+#define CC_ACTIVE_DEVSTATE_DEFAULT           AST_DEVICE_INUSE
+#define CC_CALLEE_READY_DEVSTATE_DEFAULT     AST_DEVICE_RINGING
+#define CC_CALLER_BUSY_DEVSTATE_DEFAULT      AST_DEVICE_ONHOLD
+#define CC_RECALLING_DEVSTATE_DEFAULT        AST_DEVICE_RINGING
+#define CC_COMPLETE_DEVSTATE_DEFAULT         AST_DEVICE_NOT_INUSE
+#define CC_FAILED_DEVSTATE_DEFAULT           AST_DEVICE_NOT_INUSE
+
+/*!
+ * \internal
+ * \brief initialization of defaults for CC_STATE to DEVICE_STATE map
+ */
+static enum ast_device_state cc_state_to_devstate_map[] = {
+	[CC_AVAILABLE] =        CC_AVAILABLE_DEVSTATE_DEFAULT,
+	[CC_CALLER_OFFERED] =   CC_CALLER_OFFERED_DEVSTATE_DEFAULT,
+	[CC_CALLER_REQUESTED] = CC_CALLER_REQUESTED_DEVSTATE_DEFAULT,
+	[CC_ACTIVE] =           CC_ACTIVE_DEVSTATE_DEFAULT,
+	[CC_CALLEE_READY] =     CC_CALLEE_READY_DEVSTATE_DEFAULT,
+	[CC_CALLER_BUSY] =      CC_CALLER_BUSY_DEVSTATE_DEFAULT,
+	[CC_RECALLING] =        CC_RECALLING_DEVSTATE_DEFAULT,
+	[CC_COMPLETE] =         CC_COMPLETE_DEVSTATE_DEFAULT,
+	[CC_FAILED] =           CC_FAILED_DEVSTATE_DEFAULT,
+};
+
+/*!
+ * \intenral
+ * \brief lookup the ast_device_state mapped to cc_state
+ *
+ * \return the correponding DEVICE STATE from the cc_state_to_devstate_map
+ * when passed an internal state.
+ */
+static enum ast_device_state cc_state_to_devstate(enum cc_state state)
+{
+	return cc_state_to_devstate_map[state];
+}
+
+/*!
+ * \internal
+ * \brief Callback for devicestate providers
+ *
+ * \details
+ * Initialize with ast_devstate_prov_add() and returns the corresponding
+ * DEVICE STATE based on the current CC_STATE state machine if the requested
+ * device is found and is a generic device. Returns the equivalent of
+ * CC_FAILED, which defaults to NOT_INUSE, if no device is found.  NOT_INUSE would
+ * indicate that there is no presence of any pending call back.
+ */
+static enum ast_device_state ccss_device_state(const char *device_name)
+{
+	struct cc_core_instance *core_instance;
+	unsigned long match_flags;
+	enum ast_device_state cc_current_state;
+
+	match_flags = MATCH_NO_REQUEST;
+	core_instance = ao2_t_callback_data(cc_core_instances, 0, match_agent,
+		(char *) device_name, &match_flags,
+		"Find Core Instance for ccss_device_state reqeust.");
+	if (!core_instance) {
+		ast_log_dynamic_level(cc_logger_level,
+			"Couldn't find a core instance for caller %s\n", device_name);
+		return cc_state_to_devstate(CC_FAILED);
+	}
+
+	ast_log_dynamic_level(cc_logger_level,
+		"Core %d: Found core_instance for caller %s in state %s\n",
+		core_instance->core_id, device_name, cc_state_to_string(core_instance->current_state));
+
+	if (strcmp(core_instance->agent->callbacks->type, "generic")) {
+		ast_log_dynamic_level(cc_logger_level,
+			"Core %d: Device State is only for generic agent types.\n",
+			core_instance->core_id);
+		cc_unref(core_instance, "Unref core_instance since ccss_device_state was called with native agent");
+		return cc_state_to_devstate(CC_FAILED);
+	}
+	cc_current_state = cc_state_to_devstate(core_instance->current_state);
+	cc_unref(core_instance, "Unref core_instance done with ccss_device_state");
+	return cc_current_state;
+}
+
+/*!
+ * \internal
+ * \brief Notify Device State Changes from CC STATE MACHINE
+ *
+ * \details
+ * Any time a state is changed, we call this function to notify the DEVICE STATE
+ * subsystem of the change so that subscribed phones to any corresponding hints that
+ * are using that state are updated.
+ */
+static void ccss_notify_device_state_change(const char *device, enum cc_state state)
+{
+	enum ast_device_state devstate;
+
+	devstate = cc_state_to_devstate(state);
+
+	ast_log_dynamic_level(cc_logger_level,
+		"Notification of CCSS state change to '%s', device state '%s' for device '%s'",
+		cc_state_to_string(state), ast_devstate2str(devstate), device);
+
+	ast_devstate_changed(devstate, "ccss:%s", device);
+}
+
 #define CC_OFFER_TIMER_DEFAULT			20		/* Seconds */
 #define CCNR_AVAILABLE_TIMER_DEFAULT	7200	/* Seconds */
 #define CCBS_AVAILABLE_TIMER_DEFAULT	4800	/* Seconds */
@@ -3014,6 +3120,11 @@ static int cc_do_state_change(void *datap)
 	core_instance->current_state = args->state;
 	res = state_change_funcs[core_instance->current_state](core_instance, args, previous_state);
 
+	/* If state change successful then notify any device state watchers of the change */
+	if (!res && !strcmp(core_instance->agent->callbacks->type, "generic")) {
+		ccss_notify_device_state_change(core_instance->agent->device_name, core_instance->current_state);
+	}
+
 	ast_free(args);
 	cc_unref(core_instance, "Unref since state change has completed"); /* From ao2_find */
 	return res;
@@ -4102,6 +4213,59 @@ static void initialize_cc_max_requests(void)
 	return;
 }
 
+/*!
+ * \internal
+ * \brief helper function to parse and configure each devstate map
+ */
+static void initialize_cc_devstate_map_helper(struct ast_config *cc_config, enum cc_state state, const char *cc_setting)
+{
+	const char *cc_devstate_str;
+	enum ast_device_state this_devstate;
+
+	if ((cc_devstate_str = ast_variable_retrieve(cc_config, "general", cc_setting))) {
+		this_devstate = ast_devstate_val(cc_devstate_str);
+		if (this_devstate != AST_DEVICE_UNKNOWN) {
+			cc_state_to_devstate_map[state] = this_devstate;
+		}
+	}
+}
+
+/*!
+ * \internal
+ * \brief initializes cc_state_to_devstate_map from ccss.conf
+ *
+ * \details
+ * The cc_state_to_devstate_map[] is already initialized with all the
+ * default values. This will update that structure with any changes
+ * from the ccss.conf file. The configuration parameters in ccss.conf
+ * should use any valid device state form that is recognized by
+ * ast_devstate_val() function.
+ */
+static void initialize_cc_devstate_map(void)
+{
+	struct ast_config *cc_config;
+	struct ast_flags config_flags = { 0, };
+
+	cc_config = ast_config_load2("ccss.conf", "ccss", config_flags);
+	if (!cc_config || cc_config == CONFIG_STATUS_FILEINVALID) {
+		ast_log(LOG_WARNING,
+			"Could not find valid ccss.conf file. Using cc_[state]_devstate defaults\n");
+		return;
+	}
+
+	initialize_cc_devstate_map_helper(cc_config, CC_AVAILABLE, "cc_available_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_CALLER_OFFERED, "cc_caller_offered_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_CALLER_REQUESTED, "cc_caller_requested_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_ACTIVE, "cc_active_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_CALLEE_READY, "cc_callee_ready_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_CALLER_BUSY, "cc_caller_busy_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_RECALLING, "cc_recalling_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_COMPLETE, "cc_complete_devstate");
+	initialize_cc_devstate_map_helper(cc_config, CC_FAILED, "cc_failed_devstate");
+
+	ast_config_destroy(cc_config);
+}
+
 static void cc_cli_print_monitor_stats(struct ast_cc_monitor *monitor, int fd, int parent_id)
 {
 	struct ast_cc_monitor *child_monitor_iter = monitor;
@@ -4297,9 +4461,15 @@ int ast_cc_init(void)
 	res |= ast_register_application2(cccancel_app, cccancel_exec, NULL, NULL, NULL);
 	res |= ast_cc_monitor_register(&generic_monitor_cbs);
 	res |= ast_cc_agent_register(&generic_agent_callbacks);
+
 	ast_cli_register_multiple(cc_cli, ARRAY_LEN(cc_cli));
 	cc_logger_level = ast_logger_register_level(CC_LOGGER_LEVEL_NAME);
 	dialed_cc_interface_counter = 1;
 	initialize_cc_max_requests();
+
+	/* Read the map and register the device state callback for generic agents */
+	initialize_cc_devstate_map();
+	res |= ast_devstate_prov_add("ccss", ccss_device_state);
+
 	return res;
 }