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

Jorge Eduardo Candelaria jedu at slimlogic.co.uk
Fri May 20 07:14:44 PDT 2011


On May 19, 2011, at 3:11 PM, Tanu Kaskinen wrote:

> I tried to apply the patch for easier review in meld, but unfortunately
> it seems that some helpful software has eaten the first space on lines
> that begin with one or more spaces. Because of that, "git am" doesn't
> accept the patch.
> 
> I'll review this anyway, without meld.
> 
> Overall comments: there is some confusion about whether the jack stuff
> is a core concept (hooks in pa_core) or an alsa specific thing (all jack
> related type definitions are in alsa-jack.h). I think it probably makes
> sense to keep alsa-jack.h as an alsa specific thing, but then the core
> hooks shouldn't be tied to alsa-jack.h like they are now. In the longer
> term I hope the thing how policy modules interact with jack detection
> will be monitoring the (currently not existing) available status of the
> port objects. However, if you don't want to start working on that now,

> I'd just move alsa-jack.h under pulsecore (and of course rename it).

I think the best solution for now would be to move it to pulsecore.

> 
> The only major issue in my opinion is the jack_fd reading. It should be
> made non-blocking.
> 
> -- 
> Tanu
> 
> On Thu, 2011-05-19 at 11:44 -0500, Jorge Eduardo Candelaria wrote:
>> From: Margarita Olaya Cabrera <magi at slimlogic.co.uk>
>> 
>> 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;
> 
> Instead of the card name, would it be better to store a pa_card pointer
> here? It seems that you have the card object available when you
> initialize this struct.

It seemed unnecessary since only the card name is being extracted from the
struct, but I get your point. I will change this.

> 
>> +} pa_jack_detect_t;
> 
> Structs shouldn't have the _t suffix. (Enum typedefs should, so the
> pa_jack_event_t name is correct.)
> 
> If this is alsa specific, like it seems to be, judging from the file
> location and name, I recommend prefixing the types with "pa_alsa_jack_",
> because all other alsa stuff uses "pa_alsa_" prefix too. Hmm... on the
> other hand, this struct is passed to a core hook, so maybe these types
> are not really alsa specific, and should be defined in a header under
> src/pulsecore/?

Then these structs should defined the alsa-jack.h header, since it's going
to be moved to pulsecore.

> 
> Also, I think pa_jack_event would be a better name for what you've now
> named pa_jack_detect, and the enum could be pa_jack_type_t.

Agreed, I will change this.

> 
>> +#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);
> 
> Firing core hooks directly from modules looks a bit dirty to me.

I'm actually not very familiar with hooks, however do we need to signal the jack 
insertion/removal events from jack_report().

What would be a more clean solution?

> 
>> +}
>> +
>> +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);
>> +    }
> 
> This looks like this can block indefinetely, so when trying to unload
> the module, pa__done() will get stuck in the pa_memblockq_send() call.
> You have the pa_rtpoll_item variable added to the userdata, but you
> don't seem to be using it. Did you intend to create an rtpoll item for
> the file descriptor instead of blocking in pa_read()?

Again, not very familiar with rtpoll_item, but after your comment I realized how
to properly use it to get a non_blocking thread. I will also change this now.

> 
>> +
>> +    /* 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;
>> 
> 
> 
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss at mail.0pointer.de
> https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss




More information about the pulseaudio-discuss mailing list