src: Add h2w detection code from pulseaudio-packaging

This applies changes from pulseaudio-packaging for detecting
headset & headphones via the h2w virtual switch file and udev.

Compared to pulseaudio-packaging this now forces udev as a requirement
rather than providing a compile-time switch.
This commit is contained in:
Alfred Neumayer 2019-09-19 13:15:05 +02:00 committed by Adam Boardman
parent efea2f5e7f
commit a784a5a8fa
5 changed files with 312 additions and 3 deletions

3
debian/control vendored
View file

@ -12,7 +12,8 @@ Build-Depends: android-headers (>= 23),
libltdl-dev,
libpulse-dev,
pulseaudio-dev,
libexpat1-dev
libexpat1-dev,
libudev-dev
Maintainer: Marius Gripsgard <marius@ubports.com>
Standards-Version: 4.3.0
Homepage: https://github.com/mer-hybris/pulseaudio-modules-droid

View file

@ -41,7 +41,7 @@ module_droid_source_la_LDFLAGS = -module -avoid-version -Wl,-z,noexecstack -lhyb
module_droid_source_la_LIBADD = -lm libdroid-source.la $(AM_LIBADD)
module_droid_source_la_CFLAGS = $(AM_CFLAGS)
module_droid_card_la_SOURCES = module-droid-card.c
module_droid_card_la_LDFLAGS = -module -avoid-version -Wl,-z,noexecstack -lhybris-common
module_droid_card_la_SOURCES = module-droid-card.c droid-extcon.c
module_droid_card_la_LDFLAGS = -module -avoid-version -Wl,-z,noexecstack -lhybris-common -ludev
module_droid_card_la_LIBADD = -lm libdroid-sink.la libdroid-source.la $(top_builddir)/src/common/libdroid-util.la $(AM_LIBADD)
module_droid_card_la_CFLAGS = $(AM_CFLAGS)

269
src/droid/droid-extcon.c Normal file
View file

@ -0,0 +1,269 @@
/***
This file is part of PulseAudio.
Copyright (C) 2013 Canonical Ltd.
Contact: David Henningsson
Ricardo Salveti de Araujo <ricardo.salveti@canonical.com>
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.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulsecore/core-util.h>
#include <pulsecore/device-port.h>
#include <pulsecore/i18n.h>
#include <libudev.h>
#include "droid-extcon.h"
/* For android */
#define EXTCON_NAME "switch"
/* TODO: Backport stuff to 4.0, remove before upstreaming */
#ifndef PA_PORT_AVAILABLE_YES
#define PA_PORT_AVAILABLE_YES PA_AVAILABLE_YES
#define PA_PORT_AVAILABLE_NO PA_AVAILABLE_NO
#define PA_PORT_AVAILABLE_UNKNOWN PA_AVAILABLE_UNKNOWN
#define pa_port_available_t pa_available_t
#endif
static pa_port_available_t hponly_avail(int state)
{
return (state & 2) ? PA_PORT_AVAILABLE_YES : PA_PORT_AVAILABLE_NO;
}
static pa_port_available_t hsmic_avail(int state)
{
return (state & 1) ? PA_PORT_AVAILABLE_YES : PA_PORT_AVAILABLE_NO;
}
struct droid_switch {
char *name;
uint32_t current_value;
};
static void droid_switch_free(struct droid_switch *as) {
if (!as)
return;
pa_xfree(as->name);
pa_xfree(as);
}
static struct droid_switch *droid_switch_new(const char *name) {
struct droid_switch *as = NULL;
char *filename = pa_sprintf_malloc("/sys/class/%s/%s/state", EXTCON_NAME, name);
char *state = pa_read_line_from_file(filename);
if (state == NULL) {
pa_log_debug("Cannot open '%s'. Skipping.", filename);
pa_xfree(filename);
return NULL;
}
pa_xfree(filename);
as = pa_xnew0(struct droid_switch, 1);
as->name = pa_xstrdup(name);
if (pa_atou(state, &as->current_value) < 0) {
pa_log_warn("Switch '%s' has invalid value '%s'", name, state);
pa_xfree(state);
droid_switch_free(as);
return NULL;
}
pa_log_debug("Switch '%s' opened with value '%s'", name, state);
return as;
}
struct udev_data {
struct udev *udev;
struct udev_monitor *monitor;
pa_io_event *event;
};
struct pa_droid_extcon {
pa_card *card;
struct droid_switch *h2w;
struct udev_data udev;
};
static struct droid_switch *find_matching_switch(pa_droid_extcon *u,
const char *devpath) {
if (pa_streq(devpath, "/devices/virtual/" EXTCON_NAME "/h2w"))
return u->h2w; /* To be extended if we ever support more switches */
return NULL;
}
static void notify_ports(pa_droid_extcon *u, struct droid_switch *as) {
pa_device_port *p;
void *state;
pa_assert(as == u->h2w); /* To be extended if we ever support more switches */
pa_log_debug("Value of switch %s is now %d.", as->name, as->current_value);
PA_HASHMAP_FOREACH(p, u->card->ports, state) {
if (p->direction == PA_DIRECTION_OUTPUT) {
if (!strcmp(p->name, "output-wired_headset"))
pa_device_port_set_available(p, hsmic_avail(as->current_value));
if (!strcmp(p->name, "output-wired_headphone"))
pa_device_port_set_available(p, hponly_avail(as->current_value));
}
if (p->direction == PA_DIRECTION_INPUT) {
if (!strcmp(p->name, "input-wired_headset"))
pa_device_port_set_available(p, hsmic_avail(as->current_value));
}
}
}
static void udev_cb(pa_mainloop_api *a, pa_io_event *e, int fd,
pa_io_event_flags_t events, void *userdata) {
pa_droid_extcon *u = userdata;
struct udev_device *d = udev_monitor_receive_device(u->udev.monitor);
struct udev_list_entry *entry;
struct droid_switch *as;
const char *devpath, *state;
if (!d) {
pa_log("udev_monitor_receive_device failed.");
pa_assert(a);
a->io_free(u->udev.event);
u->udev.event = NULL;
return;
}
devpath = udev_device_get_devpath(d);
if (!devpath) {
pa_log("udev_device_get_devpath failed.");
goto out;
}
pa_log_debug("Got uevent with devpath=%s", devpath);
as = find_matching_switch(u, devpath);
if (!as)
goto out;
entry = udev_list_entry_get_by_name(
udev_device_get_properties_list_entry(d), "SWITCH_STATE");
if (!entry) {
pa_log("udev_list_entry_get_by_name failed to find 'SWITCH_STATE' entry.");
goto out;
}
state = udev_list_entry_get_value(entry);
if (!state) {
pa_log("udev_list_entry_get_by_name failed.");
goto out;
}
if (pa_atou(state, &as->current_value) < 0) {
pa_log_warn("Switch '%s' has invalid value '%s'", as->name, state);
goto out;
}
notify_ports(u, as);
out:
udev_device_unref(d);
}
static bool init_udev(pa_droid_extcon *u, pa_core *core) {
int fd;
u->udev.udev = udev_new();
if (!u->udev.udev) {
pa_log("udev_new failed.");
return false;
}
u->udev.monitor = udev_monitor_new_from_netlink(u->udev.udev, "udev");
if (!u->udev.monitor) {
pa_log("udev_monitor_new_from_netlink failed.");
return false;
}
if (udev_monitor_filter_add_match_subsystem_devtype(u->udev.monitor, EXTCON_NAME, NULL) < 0) {
pa_log("udev_monitor_filter_add_match_subsystem_devtype failed.");
return false;
}
if (udev_monitor_enable_receiving(u->udev.monitor) < 0) {
pa_log("udev_monitor_enable_receiving failed.");
return false;
}
fd = udev_monitor_get_fd(u->udev.monitor);
if (fd < 0) {
pa_log("udev_monitor_get_fd failed");
return false;
}
pa_assert_se(u->udev.event = core->mainloop->io_new(core->mainloop, fd,
PA_IO_EVENT_INPUT, udev_cb, u));
return true;
}
pa_droid_extcon *pa_droid_extcon_new(pa_core *core, pa_card *card) {
pa_droid_extcon *u = pa_xnew0(pa_droid_extcon, 1);
pa_assert(core);
pa_assert(card);
u->card = card;
u->h2w = droid_switch_new("h2w");
if (!u->h2w)
goto fail;
if (!init_udev(u, core))
goto fail;
notify_ports(u, u->h2w);
return u;
fail:
pa_droid_extcon_free(u);
return NULL;
}
void pa_droid_extcon_free(pa_droid_extcon *u) {
pa_assert(u);
if (u->udev.event)
u->card->core->mainloop->io_free(u->udev.event);
if (u->udev.monitor)
udev_monitor_unref(u->udev.monitor);
if (u->udev.udev)
udev_unref(u->udev.udev);
if (u->h2w)
droid_switch_free(u->h2w);
pa_xfree(u);
}

32
src/droid/droid-extcon.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef foodroidextconhfoo
#define foodroidextconhfoo
/***
This file is part of PulseAudio.
Copyright (C) 2013 Canonical Ltd.
Contact: David Henningsson
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.
***/
typedef struct pa_droid_extcon pa_droid_extcon;
pa_droid_extcon *pa_droid_extcon_new(pa_core *, pa_card *);
void pa_droid_extcon_free(pa_droid_extcon *);
#endif

View file

@ -63,6 +63,7 @@
#include <droid/sllist.h>
#include "droid-sink.h"
#include "droid-source.h"
#include "droid-extcon.h"
#include "module-droid-card-symdef.h"
@ -160,6 +161,8 @@ struct userdata {
pa_card_profile *real_profile;
pa_droid_extcon *extcon;
pa_modargs *modargs;
pa_card *card;
};
@ -867,6 +870,8 @@ int pa__init(pa_module *m) {
pa_card_choose_initial_profile(u->card);
init_profile(u);
u->extcon = pa_droid_extcon_new(m->core, u->card);
pa_card_put(u->card);
return 0;
@ -895,6 +900,8 @@ void pa__done(pa_module *m) {
if (u->card && u->card->sources)
pa_idxset_remove_all(u->card->sources, (pa_free_cb_t) pa_droid_source_free);
if (u->extcon)
pa_droid_extcon_free(u->extcon);
if (u->card)
pa_card_free(u->card);