[PATCH 1/2] alsa: add jack detection support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Margarita Olaya Cabrera <magi@xxxxxxxxxxxxxxx>

This patch adds support to module-alsa-card so that we can read jack insertion
and removal events using the input device subsystem. i.e. we can detect
headphone insertions and removals.

This patch adds a new module parameter called jack_id that contains the ID of
the jack input device associated with this sound card. If we receive a valid
jack_id during module init then we start a reader thread that will read the
jack input device and fire a hook on every removal and insertion event.

Jack support development was kindly sponsored by Wolfson Microelectronics PLC

Signed-off-by: Margarita Olaya Cabrera <magi at slimlogic.co.uk>
Signed-off-by: Jorge Eduardo Candelaria <jedu at slimlogic.co.uk>
---
src/modules/alsa/alsa-jack.h        |   42 ++++++++++++
src/modules/alsa/module-alsa-card.c |  120 +++++++++++++++++++++++++++++++++++
src/pulsecore/core.h                |    2 +
3 files changed, 164 insertions(+), 0 deletions(-)
create mode 100644 src/modules/alsa/alsa-jack.h

diff --git a/src/modules/alsa/alsa-jack.h b/src/modules/alsa/alsa-jack.h
new file mode 100644
index 0000000..4fc67c6
--- /dev/null
+++ b/src/modules/alsa/alsa-jack.h
@@ -0,0 +1,42 @@
+#ifndef foopulsejackdetecthfoo
+#define foopulsejackdetecthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Wolfson Microelectronics PLC
+  Author Margarita Olaya <magi at 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.
+***/
+
+#include <inttypes.h>
+
+typedef enum pa_jack_event {
+    PA_JACK_HEADPHONES,
+    PA_JACK_HEADSET,
+    PA_JACK_MICROPHONE,
+    PA_JACK_LINEOUT,
+    PA_JACK_UNKNOWN,
+    PA_JACK_MAX
+} pa_jack_event_t;
+
+typedef struct pa_jack_detect {
+    pa_jack_event_t event;
+    char *card;
+} pa_jack_detect_t;
+
+#endif
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index e60aa5e..75202fb 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -29,6 +29,9 @@
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/queue.h>
+#include <pulsecore/thread.h>
+
+#include <linux/input.h>

#include <modules/reserve-wrap.h>

@@ -39,6 +42,7 @@
#include "alsa-util.h"
#include "alsa-sink.h"
#include "alsa-source.h"
+#include "alsa-jack.h"
#include "module-alsa-card-symdef.h"

PA_MODULE_AUTHOR("Lennart Poettering");
@@ -55,6 +59,7 @@ PA_MODULE_USAGE(
        "source_properties=<properties for the source> "
        "namereg_fail=<pa_namereg_register() fail parameter value> "
        "device_id=<ALSA card index> "
+        "jack_id=<Jack device index> "
        "format=<sample format> "
        "rate=<sample rate> "
        "fragments=<number of fragments> "
@@ -90,6 +95,7 @@ static const char* const valid_modargs[] = {
    "ignore_dB",
    "sync_volume",
    "profile_set",
+    "jack_id",
    NULL
};

@@ -106,6 +112,14 @@ struct userdata {
    pa_modargs *modargs;

    pa_alsa_profile_set *profile_set;
+
+     /*userdata needed for jack detection */
+    int jack_fd;
+    char *jack_id;
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
};

struct profile_data {
@@ -284,11 +298,89 @@ static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *de
    pa_xfree(t);
}

+/* Jack detection API */
+static void jack_report(struct userdata *u, struct input_event *event)
+{
+    pa_jack_detect_t jack;
+
+    jack.card = u->card->name ;
+
+    pa_log_debug("jack: card %s event type %x code %x value %x", u->card->name, event->type, event->code, event->value);
+
+    /* only process switch events */
+    if (event->type != EV_SW) {
+        pa_log_debug("jack: card %s ignored event type %x", u->card->name, event->type);
+        return;
+    }
+
+    switch (event->code) {
+    case SW_HEADPHONE_INSERT:
+        jack.event = PA_JACK_HEADPHONES;
+        break;
+    case SW_MICROPHONE_INSERT:
+        jack.event = PA_JACK_MICROPHONE;
+        break;
+    case SW_LINEOUT_INSERT:
+        jack.event = PA_JACK_LINEOUT;
+        break;
+    case SW_JACK_PHYSICAL_INSERT:
+        jack.event = PA_JACK_UNKNOWN;
+        break;
+    default:
+        pa_log_debug("jack: card %s ignored event code %x", u->card->name, event->code);
+        break;
+    }
+
+    if (event->value)
+        pa_hook_fire(&u->core->hooks[PA_CORE_HOOK_JACK_INSERT], &jack);
+    else
+        pa_hook_fire(&u->core->hooks[PA_CORE_HOOK_JACK_REMOVE], &jack);
+}
+
+static void *jack_detect_thread(void *userdata)
+{
+    struct userdata *u = userdata;
+    struct input_event event;
+
+    pa_assert(u);
+
+    pa_log_debug("jack thread started for card %s", u->card->name);
+
+    /* Install pa_thread_mq object in this thread */
+    pa_thread_mq_install(&u->thread_mq);
+
+    while (pa_read(u->jack_fd, &event, sizeof(event), NULL) == sizeof(event)) {
+       jack_report(u, &event);
+    }
+
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we receive PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+    pa_log_debug("jack thread shutting down");
+}
+
+static void jack_get_initial_state(struct userdata *u)
+{
+    struct input_event event;
+    int err;
+
+    err = ioctl(u->jack_fd, EVIOCGSW(sizeof(event)), &event);
+    if (err < 0) {
+        pa_log("Failed to read initial %s jack status %d", u->jack_id, err);
+        return;
+    }
+
+    jack_report(u, &event);
+}
+
int pa__init(pa_module *m) {
    pa_card_new_data data;
    pa_modargs *ma;
    int alsa_card_index;
    struct userdata *u;
+    struct udev *udev;
    pa_reserve_wrapper *reserve = NULL;
    const char *description;
    char *fn = NULL;
@@ -409,6 +501,26 @@ int pa__init(pa_module *m) {
                    "is abused (i.e. fixes are not pushed to ALSA), the decibel fix feature may be removed in some future "
                    "Pulseaudio version.", u->card->name);

+    /* Initialize pa_thread_mq object to communicate with jack_detect_thread */
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+    u->rtpoll_item = NULL;
+
+    /* Start the jack reader if we have a valid jack ID */
+    if (!pa_streq(pa_modargs_get_value(ma, "jack_id", NULL), "(null)")) {
+        u->jack_id = pa_sprintf_malloc("/dev/input/event%s",
+                        pa_modargs_get_value(ma, "jack_id", NULL));
+
+        /* open jack input event device */
+        if ((u->jack_fd = open(u->jack_id, O_RDONLY)) > 0) {
+            /* read the initial jack state */
+            jack_get_initial_state(u);
+
+            /* start the jack reader thread */
+            if (!(u->thread = pa_thread_new("jack-reader", jack_detect_thread, u)))
+                pa_log("Failed to create jack reader thread");
+        }
+    }
    return 0;

fail:
@@ -471,6 +583,14 @@ void pa__done(pa_module*m) {
    if (u->profile_set)
        pa_alsa_profile_set_free(u->profile_set);

+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    if (u->jack_fd)
+        pa_close(u->jack_fd);
+
    pa_xfree(u->device_id);
    pa_xfree(u);

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 358b98d..830145a 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -114,6 +114,8 @@ typedef enum pa_core_hook {
    PA_CORE_HOOK_CARD_PUT,
    PA_CORE_HOOK_CARD_UNLINK,
    PA_CORE_HOOK_CARD_PROFILE_CHANGED,
+    PA_CORE_HOOK_JACK_INSERT,
+    PA_CORE_HOOK_JACK_REMOVE,
    PA_CORE_HOOK_MAX
} pa_core_hook_t;

-- 
1.7.1



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux