diff --git a/src/Makefile.am b/src/Makefile.am
index bdedded..ef11073 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1113,7 +1113,9 @@ modlibexec_LTLIBRARIES += \
 		libalsa-util.la \
 		module-alsa-sink.la \
 		module-alsa-source.la \
-		module-alsa-card.la
+		module-alsa-card.la \
+		module-alsa-ucm.la \
+		module-alsa-jack-detect.la
 
 dist_alsaprofilesets_DATA = \
 		modules/alsa/mixer/profile-sets/default.conf \
@@ -1302,6 +1304,8 @@ SYMDEF_FILES = \
 		module-alsa-sink-symdef.h \
 		module-alsa-source-symdef.h \
 		module-alsa-card-symdef.h \
+		module-alsa-ucm-symdef.h \
+		module-alsa-jack-detect-symdef.h \
 		module-coreaudio-detect-symdef.h \
 		module-coreaudio-device-symdef.h \
 		module-solaris-symdef.h \
@@ -1610,6 +1614,16 @@ module_alsa_card_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_alsa_card_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
 module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
 
+module_alsa_ucm_la_SOURCES = modules/alsa/module-alsa-ucm.c
+module_alsa_ucm_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_ucm_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_ucm_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
+module_alsa_jack_detect_la_SOURCES = modules/alsa/module-alsa-jack-detect.c
+module_alsa_jack_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_jack_detect_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
+module_alsa_jack_detect_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
 # Solaris
 
 module_solaris_la_SOURCES = modules/module-solaris.c
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index e60aa5e..143f6a1 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -94,6 +94,7 @@ static const char* const valid_modargs[] = {
 };
 
 #define DEFAULT_DEVICE_ID "0"
+#define IS_UCM
 
 struct userdata {
     pa_core *core;
@@ -290,6 +291,7 @@ int pa__init(pa_module *m) {
     int alsa_card_index;
     struct userdata *u;
     pa_reserve_wrapper *reserve = NULL;
+    pa_bool_t is_ucm;
     const char *description;
     char *fn = NULL;
     pa_bool_t namereg_fail = FALSE;
@@ -326,10 +328,25 @@ int pa__init(pa_module *m) {
         }
     }
 
+#ifdef IS_UCM
+    is_ucm = 1;
+#endif
+
+    if (is_ucm) {
+        u->profile_set = pa_xnew0(pa_alsa_profile_set, 1);
+        u->profile_set->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        u->profile_set->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    } else {
 #ifdef HAVE_UDEV
-    fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET");
+        fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET");
 #endif
 
+        u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
+        pa_xfree(fn);
+        u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
+        pa_xfree(fn);
+    }
+
     if (pa_modargs_get_value(ma, "profile_set", NULL)) {
         pa_xfree(fn);
         fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL));
@@ -341,7 +358,9 @@ int pa__init(pa_module *m) {
     if (!u->profile_set)
         goto fail;
 
-    pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec);
+    if(!is_ucm)
+        pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec);
+
     pa_alsa_profile_set_dump(u->profile_set);
 
     pa_card_new_data_init(&data);
@@ -371,15 +390,20 @@ int pa__init(pa_module *m) {
             pa_reserve_wrapper_set_application_device_name(reserve, description);
 
     data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    add_profiles(u, data.profiles);
 
-    if (pa_hashmap_isempty(data.profiles)) {
-        pa_log("Failed to find a working profile.");
-        pa_card_new_data_done(&data);
-        goto fail;
-    }
+    if (is_ucm)
+        /* Sets verbs here ???? */
+	printf("What are the profiles\n");
+    else {
+        add_profiles(u, data.profiles);
 
-    add_disabled_profile(data.profiles);
+        if (pa_hashmap_isempty(data.profiles)) {
+            pa_log("Failed to find a working profile.");
+            pa_card_new_data_done(&data);
+            goto fail;
+        }
+        add_disabled_profile(data.profiles);
+    }
 
     if (pa_modargs_get_proplist(ma, "card_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
         pa_log("Invalid properties");
@@ -396,7 +420,8 @@ int pa__init(pa_module *m) {
     u->card->userdata = u;
     u->card->set_profile = card_set_profile;
 
-    init_profile(u);
+    if (!is_ucm)
+        init_profile(u);
 
     if (reserve)
         pa_reserve_wrapper_unref(reserve);
diff --git a/src/modules/alsa/module-alsa-jack-detect.c b/src/modules/alsa/module-alsa-jack-detect.c
new file mode 100644
index 0000000..c1da0e3
--- /dev/null
+++ b/src/modules/alsa/module-alsa-jack-detect.c
@@ -0,0 +1,172 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Wolfson Microelectronics PLC
+ Author Margarita Olaya <magi@slimlogic.co.uk>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+
+ Jack detect integration was kindly sponsored by Wolfson Microelectronics PLC.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulse/proplist.h>
+
+#include "alsa-util.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <pthread.h>
+
+#include "module-alsa-jack-detect-symdef.h"
+
+PA_MODULE_AUTHOR("Margarita Olaya");
+PA_MODULE_DESCRIPTION("ALSA Jack Detect");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+    "device_id=<ALSA jack device> ");
+
+static const char* const valid_modargs[] = {
+    "card_name",
+    "device_id",
+    NULL
+};
+
+#define DEFAULT_DEVICE_ID "0"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    char *device_id;
+	int fd;
+
+    pa_card *card;
+    pthread_t reader;
+};
+
+static void * jack_detect_thread(void *tdata)
+{
+    struct userdata *u = tdata;
+    struct input_event event;
+
+	printf("jack thread started\n");
+	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+    while (read(u->fd, &event, sizeof(event)) == sizeof(event)) {
+
+		printf("read type %x code %x value %x\n", event.code, event.type, event.value);
+
+		/* only process switch events */
+		if (event.type != EV_SW) {
+			printf("ignored event type %x\n", event.type);
+            continue;
+        }
+
+        switch (event.code) {
+        case SW_HEADPHONE_INSERT:
+			if (event.value)
+				printf("headphone insert\n");
+			else
+				printf("headphone removed\n");
+			break;
+		case SW_MICROPHONE_INSERT:
+		case SW_LINEOUT_INSERT:
+		case SW_JACK_PHYSICAL_INSERT:
+		default:
+			printf("ignored event code %x\n", event.code);
+			break;
+		}
+    }
+
+    printf("jack thread stopped\n");
+    return NULL;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma;
+    struct userdata *u;
+
+    printf("This is jack module\n");
+    pa_alsa_refcnt_inc();
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
+
+	/* open the input event device */
+	printf("opening %s\n", u->device_id);
+    if ((u->fd = open(u->device_id, O_RDONLY)) < 0)
+		goto fail_jack;
+
+	/* start the jack reader thread */
+	pa_assert_se(pthread_create(&u->reader, NULL, jack_detect_thread, u) == 0);
+
+    return 0;
+
+fail_jack:
+    pa_xfree(u->device_id);
+    pa_xfree(u);
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    pa_log_debug("Out of jack module");
+
+	 if (!(u = m->userdata))
+        goto finish;
+
+    /* wait for reader thread to finish */
+    pthread_cancel(u->reader);
+    pthread_join(u->reader, NULL);
+    close(u->fd);
+
+    if (u->card)
+        pa_card_free(u->card);
+
+    pa_xfree(u->device_id);
+    pa_xfree(u);
+
+finish:
+    pa_alsa_refcnt_dec();
+}
diff --git a/src/modules/alsa/module-alsa-ucm.c b/src/modules/alsa/module-alsa-ucm.c
new file mode 100644
index 0000000..a49eb53
--- /dev/null
+++ b/src/modules/alsa/module-alsa-ucm.c
@@ -0,0 +1,450 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2011 Wolfson Microelectronics PLC
+ Author Margarita Olaya <magi@slimlogic.co.uk>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+
+ UCM integration was kindly sponsored by Wolfson Microelectronics PLC.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulse/proplist.h>
+
+#include "alsa-util.h"
+#include <alsa/use-case.h>
+#include "module-alsa-ucm-symdef.h"
+
+PA_MODULE_AUTHOR("Margarita Olaya");
+PA_MODULE_DESCRIPTION("ALSA UCM");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+    "device_id=<ALSA card index> ");
+
+static const char* const valid_modargs[] = {
+    "card_name",
+    "device_id",
+    NULL
+};
+
+#define DEFAULT_DEVICE_ID "0"
+#define IS_UCM 1
+#define PROP_COUNT 7
+#define UCM_MAX_DEVICES 16
+#define UCM_MAX_MODIFIERS 16
+#define UCM_MAX_VERBS 16
+#define UCM_MAX_UCASES 5
+#define VERB_SET 1
+
+struct ucm_device {
+    pa_proplist *proplist;
+    int status;
+};
+
+struct ucm_modifier {
+    pa_proplist *proplist;
+    int status;
+};
+
+struct ucm_verb {
+    struct ucm_device devices[UCM_MAX_DEVICES];
+    struct ucm_modifier modifiers[UCM_MAX_MODIFIERS];
+    pa_proplist *proplist;
+    int status;
+};
+
+struct ucm_config {
+    snd_use_case_mgr_t *uc_mgr;
+    struct ucm_verb verb[UCM_MAX_VERBS];
+    const char *verb_ini;
+    const char *verb_new;
+    int status;
+    int num_verbs;
+    int verb_count;
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    struct ucm_config uc[UCM_MAX_UCASES];
+
+    char *device_id;
+
+    pa_hook_slot
+        *sink_input_new_hook_slot,
+        *source_output_new_hook_slot,
+        *sink_put_hook_slot,
+        *source_put_hook_slot,
+        *sink_input_unlink_slot,
+        *source_output_unlink_slot;
+
+    pa_card *card;
+};
+
+static int pa_ucm_get_property(struct ucm_verb *current_verb, snd_use_case_mgr_t *uc_mgr) {
+    const char *value;
+    int i;
+    int err;
+
+    char identifier[PROP_COUNT][18] = {
+        "PlaybackPCM",
+        "CapturePCM",
+        "PlaybackVolume",
+        "PlaybackSwitch",
+        "CaptureVolume",
+        "CaptureSwitch",
+        "TQ",
+    };
+
+    char property[PROP_COUNT][20] = {
+        PA_PROP_UCM_SINK,
+        PA_PROP_UCM_SOURCE,
+        PA_PROP_UCM_PLAYBACK_VOLUME,
+        PA_PROP_UCM_PLAYBACK_SWITCH,
+        PA_PROP_UCM_CAPTURE_VOLUME,
+        PA_PROP_UCM_CAPTURE_SWITCH,
+        PA_PROP_UCM_QOS,
+    };
+
+    for (i = 0; i < PROP_COUNT; i++) {
+        err = snd_use_case_get(uc_mgr, identifier[i], &value);
+        if (err < 0 )
+            pa_log_info("%s is not defined", identifier[i]);
+
+        pa_proplist_sets(current_verb->proplist, property[i], value);
+    }
+    return 0;
+};
+
+static int pa_ucm_get_devices(struct ucm_verb *current_verb, snd_use_case_mgr_t *uc_mgr) {
+    struct ucm_device *current_device;
+    const char **dev_list;
+    int num, i, j = 0;
+    const char *device;
+
+    num = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
+    if (num < 0)
+        return num;
+
+    for (i = 0; i < num; i += 2) {
+        current_device = &current_verb->devices[j];
+        current_device->proplist = pa_proplist_new();
+        pa_proplist_sets(current_device->proplist, PA_PROP_UCM_NAME, dev_list[i]);
+        /* TODO: Remove this is only for debug */
+        device = pa_proplist_gets(current_device->proplist, PA_PROP_UCM_NAME);
+        pa_log_debug(" The devices is %s", device);
+        j++;
+    }
+    return 0;
+};
+
+static int pa_ucm_get_modifiers(struct ucm_verb *current_verb, snd_use_case_mgr_t *uc_mgr) {
+    struct ucm_modifier *current_modifier;
+    const char **mod_list;
+    int num, i, j = 0;
+    const char *modifier;
+
+    num = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
+    if (num < 0)
+        return num;
+
+    for (i = 0; i < num; i += 2) {
+        current_modifier = &current_verb->modifiers[j];
+        current_modifier->proplist = pa_proplist_new();
+        pa_proplist_sets(current_modifier->proplist, PA_PROP_UCM_NAME, mod_list[i]);
+        pa_log_debug("The modifier is %s", modifier);
+        j++;
+    }
+    return 0;
+};
+
+static int pa_ucm_get_properties(struct ucm_verb *current_verb, snd_use_case_mgr_t *uc_mgr, const char *verb) {
+    int err;
+
+    err = snd_use_case_set(uc_mgr, "_verb", verb);
+    if (err < 0)
+        return err;
+
+    err = pa_ucm_get_devices(current_verb, uc_mgr);
+    if (err < 0)
+        pa_log("Failed to get UCM devices for verb %s", verb);
+
+    err = pa_ucm_get_modifiers(current_verb, uc_mgr);
+    if (err < 0)
+        pa_log("Failed to get UCM modifiers for verb %s", verb);
+
+    pa_ucm_get_property(current_verb, uc_mgr);
+
+    return 0;
+}
+
+static int pa_ucm_set_verb(struct userdata *u, const char *role) {
+    snd_use_case_mgr_t *uc_mgr;
+    struct ucm_verb *current_verb;
+    struct ucm_config *uc;
+    const char *verb;
+    int alsa_card_index, num, err = 0;
+
+    if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
+        pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index));
+    }
+
+    uc = &u->uc[alsa_card_index];
+    num = uc->num_verbs;
+    uc_mgr = uc->uc_mgr;
+
+    for (int i = 0; i < 2; i++) {
+        current_verb = &uc->verb[i];
+        verb = pa_proplist_gets(current_verb->proplist, PA_PROP_UCM_NAME);
+        if (!strcmp(verb,role)) {
+            pa_log_debug("Set %s verb", verb);
+            goto set_verb;
+        }
+    }
+
+    pa_log_warn("Verb and role didn't match. Set HiFi per default");
+    verb = "HiFi";
+
+set_verb:
+    if (!current_verb->status) {
+        err = snd_use_case_set(uc_mgr, "_verb", verb);
+        if (err < 0)
+            return err;
+
+        current_verb->status = VERB_SET;
+    }
+
+    if (uc->verb_count == 0)
+        uc->verb_ini = verb;
+    else
+        uc->verb_new = verb;
+
+    uc->verb_count++;
+
+    return 0;
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    const char *role;
+    int err;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    pa_log_debug("calling ucm module");
+
+    if (!new_data->proplist) {
+        pa_log_debug("New stream lacks property data");
+        return PA_HOOK_OK;
+    }
+
+    if (new_data->sink) {
+        pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
+        pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    err = pa_ucm_set_verb(u, role);
+    if (err < 0)
+	return PA_HOOK_CANCEL;
+
+    return PA_HOOK_OK;
+}
+
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    const char *role;
+    int err;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    pa_log_debug("At UCM module\n");
+
+    if (!new_data->proplist) {
+        pa_log_debug("New stream lacks property data.");
+        return PA_HOOK_OK;
+    }
+
+    if (new_data->source) {
+        pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
+        pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
+        return PA_HOOK_OK;
+    }
+
+    err = pa_ucm_set_verb(u, role);
+    if (err < 0)
+	return PA_HOOK_CANCEL;
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    snd_use_case_mgr_t *uc_mgr;
+    struct ucm_config *uc;
+    int alsa_card_index, err;
+
+    if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
+        pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index));
+        return alsa_card_index;
+    }
+
+    uc = &u->uc[alsa_card_index];
+    uc_mgr = uc->uc_mgr;
+
+    /* The hook can be call twice for one stream */
+    if (uc->verb_count != 0)
+        uc->verb_count--;
+
+    if (uc->verb_count == 0) {
+        return PA_HOOK_OK;
+    }
+
+    err = snd_use_case_set(uc_mgr, "_verb", uc->verb_ini);
+    if (err < 0)
+        return err;
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_cb(pa_core *core, pa_source_output *s, struct userdata *u) {
+    printf("MUCM %s %d\n", __func__, __LINE__);
+    return PA_HOOK_OK;
+}
+
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma;
+    snd_use_case_mgr_t *uc_mgr;
+    const char **verb_list;
+    int alsa_card_index, err, num, j = 0;
+    struct userdata *u;
+    char *cn;
+
+    pa_log_info("This is ucm module");
+    pa_alsa_refcnt_inc();
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
+
+    /* A little bit later than module-stream-restore */
+    u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+    u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
+    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
+
+    if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
+        pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index));
+        goto fail;
+    }
+
+    snd_card_get_name(alsa_card_index, &cn);
+    err = snd_use_case_mgr_open(&uc_mgr, cn);
+    if (err < 0) {
+        pa_log("Unable to open usecase");
+        goto fail;
+    } else {
+        u->uc[alsa_card_index].uc_mgr = uc_mgr;
+        u->uc[alsa_card_index].status = IS_UCM;
+        pa_log_debug("Open ucm for card %s", cn);
+    }
+
+    num = snd_use_case_verb_list(uc_mgr, &verb_list);
+    if (num <= 0) {
+        pa_log("Can not get verb list");
+        return 0;
+    }
+
+    u->uc[alsa_card_index].num_verbs =  num;
+    for (int i = 0; i < num; i += 2) {
+        const char *verb = verb_list[i];
+        struct ucm_verb *current_verb;
+
+        current_verb = &u->uc[alsa_card_index].verb[j];
+        current_verb->proplist = pa_proplist_new();
+        j++;
+        pa_proplist_sets(current_verb->proplist, PA_PROP_UCM_NAME, verb);
+
+        /* Get devices and modifiers for each verb */
+        err = pa_ucm_get_properties(current_verb, uc_mgr, verb);
+        if (err < 0)
+            pa_log("Failed to set the verb %s", verb);
+    }
+
+    u->uc[alsa_card_index].verb_count = 0;
+    return 0;
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    pa_log_debug("Out of ucm module");
+    if (!(u = m->userdata))
+        goto finish;
+
+    if (u->sink_input_new_hook_slot)
+        pa_hook_slot_free(u->sink_input_new_hook_slot);
+    if (u->source_output_new_hook_slot)
+        pa_hook_slot_free(u->source_output_new_hook_slot);
+    if (u->sink_input_unlink_slot)
+        pa_hook_slot_free(u->sink_input_unlink_slot);
+    if (u->source_output_unlink_slot)
+        pa_hook_slot_free(u->source_output_unlink_slot);
+
+    if (u->card)
+        pa_card_free(u->card);
+
+    pa_xfree(u->device_id);
+    pa_xfree(u);
+
+finish:
+    pa_alsa_refcnt_dec();
+}
diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c
index 63ad195..f5f2eb9 100644
--- a/src/modules/module-udev-detect.c
+++ b/src/modules/module-udev-detect.c
@@ -60,6 +60,7 @@ struct device {
 struct userdata {
     pa_core *core;
     pa_hashmap *devices;
+    pa_hashmap *input_devices;
 
     pa_bool_t use_tsched:1;
     pa_bool_t ignore_dB:1;
@@ -71,6 +72,9 @@ struct userdata {
 
     int inotify_fd;
     pa_io_event *inotify_io;
+
+    int inotify_input_fd;
+    pa_io_event *inotify_input_io;
 };
 
 static const char* const valid_modargs[] = {
@@ -81,6 +85,7 @@ static const char* const valid_modargs[] = {
 };
 
 static int setup_inotify(struct userdata *u);
+static int setup_input_notify(struct userdata *u);
 
 static void device_free(struct device *d) {
     pa_assert(d);
@@ -163,6 +168,21 @@ static pa_bool_t pcm_is_modem(const char *card_idx, const char *pcm) {
     return is_modem;
 }
 
+static const char *path_get_input_id(const char *path) {
+    const char *e;
+
+    if (!path)
+        return NULL;
+
+    if (!(e = strrchr(path, '/')))
+        return NULL;
+
+    if (!pa_startswith(e, "/event"))
+        return NULL;
+
+    return e + 6;
+}
+
 static pa_bool_t is_card_busy(const char *id) {
     char *card_path = NULL, *pcm_path = NULL, *sub_status = NULL;
     DIR *card_dir = NULL, *pcm_dir = NULL;
@@ -273,6 +293,7 @@ fail:
 
 static void verify_access(struct userdata *u, struct device *d) {
     char *cd;
+    char *args;
     pa_card *card;
     pa_bool_t accessible;
 
@@ -324,6 +345,20 @@ static void verify_access(struct userdata *u, struct device *d) {
                  * during opening was canceled by a "try again"
                  * failure or a "fatal" failure. */
 
+                args = pa_sprintf_malloc("device_id=\"%s\""
+                                         "card_name=\"%s\" ",
+                                         path_get_card_id(d->path),
+                                         d->card_name);
+
+                pa_log_debug("Loading module-alsa-ucm with arguments '%s'\n", args);
+                m = pa_module_load(u->core, "module-alsa-ucm", args);
+
+                if (m) {
+                      d->module = m->index;
+                      printf("Card %s (%s) module loaded.", d->path, d->card_name);
+                } else
+                      printf("Card %s (%s) failed to load module.", d->path, d->card_name);
+
                 if (pa_ratelimit_test(&d->ratelimit, PA_LOG_DEBUG)) {
                     pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
                     m = pa_module_load(u->core, "module-alsa-card", d->args);
@@ -339,7 +374,9 @@ static void verify_access(struct userdata *u, struct device *d) {
                                 d->card_name,
                                 d->ratelimit.burst,
                                 (long long unsigned) (d->ratelimit.interval / PA_USEC_PER_SEC));
+
             }
+
         }
 
     } else {
@@ -352,6 +389,47 @@ static void verify_access(struct userdata *u, struct device *d) {
     }
 }
 
+static void verify_input_access(struct userdata *u, struct device *d) {
+    char *cd;
+    char *args;
+    pa_card *card;
+    pa_bool_t accessible;
+
+    pa_assert(u);
+    pa_assert(d);
+
+    cd = pa_sprintf_malloc("%s/input/event%s", udev_get_dev_path(u->udev), path_get_input_id(d->path));
+    accessible = access(cd, R_OK) >= 0;
+    pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));
+
+    if (d->module == PA_INVALID_INDEX) {
+
+        /* If we are not loaded, try to load */
+        if (accessible) {
+            pa_module *m;
+
+            args = pa_sprintf_malloc("device_id=\"%s\""
+                            "card_name=\"%s\" ", cd, d->card_name);
+
+            pa_log_debug("Loading module-alsa-jack detect with arguments '%s'", args);
+            m = pa_module_load(u->core, "module-alsa-jack-detect", args);
+            if (m) {
+                  d->module = m->index;
+                  pa_log_info("Card %s (%s) jack module loaded.", d->path, d->card_name);
+            } else
+                  pa_log_info("Card %s (%s) failed to load jack module.", d->path, d->card_name);
+        }
+    } else {
+        /* TODO: do we need this for Jack detect ??? */
+        /* If we are already loaded update suspend status with
+         * accessible boolean */
+
+        if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
+            pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
+    }
+    pa_xfree(cd);
+}
+
 static void card_changed(struct userdata *u, struct udev_device *dev) {
     struct device *d;
     const char *path;
@@ -364,6 +442,8 @@ static void card_changed(struct userdata *u, struct udev_device *dev) {
     /* Maybe /dev/snd is now available? */
     setup_inotify(u);
 
+    setup_input_notify(u);
+
     path = udev_device_get_devpath(dev);
 
     if ((d = pa_hashmap_get(u->devices, path))) {
@@ -421,6 +501,75 @@ static void remove_card(struct userdata *u, struct udev_device *dev) {
     device_free(d);
 }
 
+static void input_changed(struct userdata *u, struct udev_device *dev) {
+    struct device *d;
+    const char *path;
+    const char *t;
+    char *n;
+
+    pa_assert(u);
+    pa_assert(dev);
+
+    /* Maybe /dev/snd is now available? */
+    setup_inotify(u);
+
+    setup_input_notify(u);
+
+    path = udev_device_get_devpath(dev);
+
+    if ((d = pa_hashmap_get(u->input_devices, path))) {
+        verify_input_access(u, d);
+        return;
+    }
+
+    d = pa_xnew0(struct device, 1);
+    d->path = pa_xstrdup(path);
+    d->module = PA_INVALID_INDEX;
+    PA_INIT_RATELIMIT(d->ratelimit, 10*PA_USEC_PER_SEC, 5);
+
+    /* TODO: do we need all this card info and checks for input devices ??? */
+    if (!(t = udev_device_get_property_value(dev, "PULSE_NAME")))
+        if (!(t = udev_device_get_property_value(dev, "ID_ID")))
+            if (!(t = udev_device_get_property_value(dev, "ID_PATH")))
+                t = path_get_card_id(path);
+
+    n = pa_namereg_make_valid_name(t);
+    d->card_name = pa_sprintf_malloc("alsa_card.%s", n);
+    d->args = pa_sprintf_malloc("device_id=\"%s\" "
+                                "name=\"%s\" "
+                                "card_name=\"%s\" "
+                                "tsched=%s "
+                                "ignore_dB=%s "
+                                "card_properties=\"module-udev-detect.discovered=1\"",
+                                path_get_card_id(path),
+                                n,
+                                d->card_name,
+                                pa_yes_no(u->use_tsched),
+                                pa_yes_no(u->ignore_dB));
+    pa_xfree(n);
+
+    pa_hashmap_put(u->input_devices, d->path, d);
+
+    verify_input_access(u, d);
+}
+
+static void remove_input(struct userdata *u, struct udev_device *dev) {
+    struct device *d;
+
+    pa_assert(u);
+    pa_assert(dev);
+
+    if (!(d = pa_hashmap_remove(u->input_devices, udev_device_get_devpath(dev))))
+        return;
+
+    pa_log_info("Input %s removed.", d->path);
+
+    if (d->module != PA_INVALID_INDEX)
+        pa_module_unload_request_by_index(u->core, d->module, TRUE);
+
+    device_free(d);
+}
+
 static void process_device(struct userdata *u, struct udev_device *dev) {
     const char *action, *ff;
 
@@ -438,21 +587,31 @@ static void process_device(struct userdata *u, struct udev_device *dev) {
         return;
     }
 
-    action = udev_device_get_action(dev);
-
-    if (action && pa_streq(action, "remove"))
-        remove_card(u, dev);
-    else if ((!action || pa_streq(action, "change")) && udev_device_get_property_value(dev, "SOUND_INITIALIZED"))
-        card_changed(u, dev);
-
-    /* For an explanation why we don't look for 'add' events here
-     * have a look into /lib/udev/rules.d/78-sound-card.rules! */
+    if (path_get_card_id(udev_device_get_devpath(dev))) {
+        if (action && pa_streq(action, "remove"))
+            remove_card(u, dev);
+        else if ((!action || pa_streq(action, "change")) &&
+                 udev_device_get_property_value(dev, "SOUND_INITIALIZED"))
+            card_changed(u, dev);
+
+        /* For an explanation why we don't look for 'add' events here
+         * have a look into /lib/udev/rules.d/78-sound-card.rules! */
+
+    } else if (path_get_input_id(udev_device_get_devpath(dev)) &&
+               strstr(udev_device_get_property_value(dev, "DEVPATH"), "sound")) {
+        /* input devices for Jack insertion are handled slightly differently */
+        if (action && pa_streq(action, "remove"))
+            remove_input(u, dev);
+        else if (!action || pa_streq(action, "change") ||
+                 pa_streq(action, "add"))
+            input_changed(u, dev);
+    }
 }
 
 static void process_path(struct userdata *u, const char *path) {
     struct udev_device *dev;
 
-    if (!path_get_card_id(path))
+    if (!path_get_card_id(path) && !path_get_input_id(path))
         return;
 
     if (!(dev = udev_device_new_from_syspath(u->udev, path))) {
@@ -481,13 +640,11 @@ static void monitor_cb(
         goto fail;
     }
 
-    if (!path_get_card_id(udev_device_get_devpath(dev))) {
+    if (path_get_card_id(udev_device_get_devpath(dev)) ||
+        path_get_input_id(udev_device_get_devpath(dev))) {
+        process_device(u, dev);
         udev_device_unref(dev);
-        return;
     }
-
-    process_device(u, dev);
-    udev_device_unref(dev);
     return;
 
 fail:
@@ -523,6 +680,21 @@ static pa_bool_t control_node_belongs_to_device(
     return b;
 }
 
+static pa_bool_t input_node_belongs_to_device(
+        struct device *d,
+        const char *node) {
+
+    char *cd;
+    pa_bool_t b;
+
+    cd = pa_sprintf_malloc("event%s", path_get_input_id(d->path));
+
+    b = pa_streq(node, cd);
+    pa_xfree(cd);
+
+    return b;
+}
+
 static void inotify_cb(
         pa_mainloop_api*a,
         pa_io_event* e,
@@ -615,6 +787,86 @@ fail:
     }
 }
 
+static void input_notify_cb(
+        pa_mainloop_api*a,
+        pa_io_event* e,
+        int fd,
+        pa_io_event_flags_t events,
+        void *userdata) {
+
+    struct {
+        struct inotify_event e;
+        char name[NAME_MAX];
+    } buf;
+    struct userdata *u = userdata;
+    static int type = 0;
+    pa_bool_t deleted = FALSE;
+    struct device *d;
+    void *state;
+
+    for (;;) {
+        ssize_t r;
+        struct inotify_event *event;
+
+        pa_zero(buf);
+        if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) {
+            if (r < 0 && errno == EAGAIN)
+                break;
+            pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
+            goto fail;
+        }
+
+        event = &buf.e;
+        while (r > 0) {
+            size_t len;
+            if ((size_t) r < sizeof(struct inotify_event)) {
+                pa_log("read() too short.");
+                goto fail;
+            }
+
+            len = sizeof(struct inotify_event) + event->len;
+            if ((size_t) r < len) {
+                pa_log("Payload missing.");
+                goto fail;
+            }
+
+            /* From udev we get the guarantee that the control
+             * device's ACL is changed last. To avoid races when ACLs
+             * are changed we hence watch only the control device */
+            if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "event")))
+                PA_HASHMAP_FOREACH(d, u->input_devices, state)
+                    if (input_node_belongs_to_device(d, event->name))
+                        d->need_verify = TRUE;
+
+            /* /dev/input/ might have been removed */
+            if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF)))
+                deleted = TRUE;
+            event = (struct inotify_event*) ((uint8_t*) event + len);
+            r -= len;
+        }
+    }
+
+    PA_HASHMAP_FOREACH(d, u->input_devices, state)
+        if (d->need_verify) {
+            d->need_verify = FALSE;
+            verify_input_access(u, d);
+        }
+
+    if (!deleted)
+        return;
+
+fail:
+    if (u->inotify_input_io) {
+        a->io_free(u->inotify_input_io);
+        u->inotify_input_io = NULL;
+    }
+
+    if (u->inotify_input_fd >= 0) {
+        pa_close(u->inotify_input_fd);
+        u->inotify_input_fd = -1;
+    }
+}
+
 static int setup_inotify(struct userdata *u) {
     char *dev_snd;
     int r;
@@ -659,6 +911,44 @@ static int setup_inotify(struct userdata *u) {
     return 0;
 }
 
+static int setup_input_notify(struct userdata *u) {
+    char *dev_input;
+    int r;
+
+    if (u->inotify_input_fd >= 0)
+        return 0;
+
+    if ((u->inotify_input_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
+        pa_log("inotify_init1() failed: %s", pa_cstrerror(errno));
+        return -1;
+    }
+
+    dev_input = pa_sprintf_malloc("%s/input", udev_get_dev_path(u->udev));
+    r = inotify_add_watch(u->inotify_input_fd, dev_input, IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
+    pa_xfree(dev_input);
+    if (r < 0) {
+        int saved_errno = errno;
+        pa_close(u->inotify_input_fd);
+        u->inotify_input_fd = -1;
+
+        if (saved_errno == ENOENT) {
+            pa_log_debug("/dev/input/ is apparently not existing yet, retrying to create inotify watch later.");
+            return 0;
+        }
+
+        if (saved_errno == ENOSPC) {
+            pa_log("You apparently ran out of inotify watches, probably because Tracker/Beagle took them all away.");
+            return 0;
+        }
+
+        pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno));
+        return -1;
+    }
+    pa_assert_se(u->inotify_input_io = u->core->mainloop->io_new(u->core->mainloop, u->inotify_input_fd, PA_IO_EVENT_INPUT, input_notify_cb, u));
+
+    return 0;
+}
+
 int pa__init(pa_module *m) {
     struct userdata *u = NULL;
     pa_modargs *ma;
@@ -678,7 +968,9 @@ int pa__init(pa_module *m) {
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    u->input_devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     u->inotify_fd = -1;
+    u->inotify_input_fd = -1;
 
     if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
         pa_log("Failed to parse tsched= argument.");
@@ -706,6 +998,9 @@ int pa__init(pa_module *m) {
     if (setup_inotify(u) < 0)
         goto fail;
 
+    if (setup_input_notify(u) < 0)
+        goto fail;
+
     if (!(u->monitor = udev_monitor_new_from_netlink(u->udev, "udev"))) {
         pa_log("Failed to initialize monitor.");
         goto fail;
@@ -743,6 +1038,11 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
+     if (udev_enumerate_add_match_subsystem(enumerate, "input") < 0) {
+        pa_log("Failed to match to subsystem.");
+        goto fail;
+    }
+
     if (udev_enumerate_scan_devices(enumerate) < 0) {
         pa_log("Failed to scan for devices.");
         goto fail;
@@ -796,6 +1096,12 @@ void pa__done(pa_module *m) {
     if (u->inotify_fd >= 0)
         pa_close(u->inotify_fd);
 
+    if (u->inotify_input_io)
+        m->core->mainloop->io_free(u->inotify_input_io);
+
+    if (u->inotify_input_fd >= 0)
+        pa_close(u->inotify_input_fd);
+
     if (u->devices) {
         struct device *d;
 
@@ -805,5 +1111,14 @@ void pa__done(pa_module *m) {
         pa_hashmap_free(u->devices, NULL, NULL);
     }
 
+   if (u->input_devices) {
+        struct device *d;
+
+        while ((d = pa_hashmap_steal_first(u->input_devices)))
+            device_free(d);
+
+        pa_hashmap_free(u->input_devices, NULL, NULL);
+    }
+
     pa_xfree(u);
 }
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 5db3ada..26429d7 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -245,6 +245,33 @@ PA_C_DECL_BEGIN
 /** For modules: a version string for the module. e.g. "0.9.15" */
 #define PA_PROP_MODULE_VERSION                 "module.version"
 
+/** For devices: List of verbs, devices or modifiers availables */
+#define PA_PROP_UCM_NAME	"ucm.name"
+
+/** For devices: List of supported devices per verb*/
+#define PA_PROP_UCM_DESCRIPTION		"ucm.description"
+
+/** For devices: Playback device name e.g PlaybackPCM */
+#define PA_PROP_UCM_SINK		"ucm.sink"
+
+/** For devices: Capture device name e.g CapturePCM*/
+#define PA_PROP_UCM_SOURCE		"ucm.source"
+
+/** For devices: Playback control volume ID string. e.g PlaybackVolume */
+#define PA_PROP_UCM_PLAYBACK_VOLUME	"ucm.playback.volume"
+
+/** For devices: Playback switch e.g PlaybackSwitch */
+#define PA_PROP_UCM_PLAYBACK_SWITCH	"ucm.playback.switch"
+
+/** For devices: Capture controls volume ID string. e.g CaptureVolume */
+#define PA_PROP_UCM_CAPTURE_VOLUME	"ucm.capture.volume"
+
+/** For devices: Capture switch e.g CaptureSwitch */
+#define PA_PROP_UCM_CAPTURE_SWITCH	"ucm.capture.switch"
+
+/** For devices: Quality of Service */
+#define PA_PROP_UCM_QOS			"ucm.qos"
+
 /** A property list object. Basically a dictionary with ASCII strings
  * as keys and arbitrary data as values. \since 0.9.11 */
 typedef struct pa_proplist pa_proplist;
