1
From dc9dfecfbe2268c48916a22e9d3e92f452a00762 Mon Sep 17 00:00:00 2001
2
From: David Henningsson <david.henningsson@canonical.com>
3
Date: Fri, 19 Jul 2013 10:50:07 +0200
4
Subject: [PATCH 1/2] ALSA: Add extcon (Android switch) jack detection
6
For headphone/headset only, so far.
9
src/modules/alsa/alsa-extcon.c | 283 +++++++++++++++++++++++++++++++++++
10
src/modules/alsa/alsa-extcon.h | 33 ++++
11
src/modules/alsa/alsa-ucm.c | 4 +-
12
src/modules/alsa/alsa-ucm.h | 1 +
13
src/modules/alsa/module-alsa-card.c | 5 +
14
6 files changed, 325 insertions(+), 2 deletions(-)
15
create mode 100644 src/modules/alsa/alsa-extcon.c
16
create mode 100644 src/modules/alsa/alsa-extcon.h
18
diff --git a/src/Makefile.am b/src/Makefile.am
19
index a621a30..471dc72 100644
22
@@ -1704,6 +1704,7 @@ module_coreaudio_device_la_LIBADD = $(MODULE_LIBADD)
23
libalsa_util_la_SOURCES = \
24
modules/alsa/alsa-util.c modules/alsa/alsa-util.h \
25
modules/alsa/alsa-ucm.c modules/alsa/alsa-ucm.h \
26
+ modules/alsa/alsa-extcon.c modules/alsa/alsa-extcon.h \
27
modules/alsa/alsa-mixer.c modules/alsa/alsa-mixer.h \
28
modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h \
29
modules/alsa/alsa-source.c modules/alsa/alsa-source.h \
30
diff --git a/src/modules/alsa/alsa-extcon.c b/src/modules/alsa/alsa-extcon.c
32
index 0000000..5c60137
34
+++ b/src/modules/alsa/alsa-extcon.c
37
+ This file is part of PulseAudio.
39
+ Copyright 2013 David Henningsson, Canonical Ltd.
41
+ PulseAudio is free software; you can redistribute it and/or modify
42
+ it under the terms of the GNU Lesser General Public License as published
43
+ by the Free Software Foundation; either version 2.1 of the License,
44
+ or (at your option) any later version.
46
+ PulseAudio is distributed in the hope that it will be useful, but
47
+ WITHOUT ANY WARRANTY; without even the implied warranty of
48
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
49
+ General Public License for more details.
51
+ You should have received a copy of the GNU Lesser General Public License
52
+ along with PulseAudio; if not, write to the Free Software
53
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
61
+#include <pulsecore/core-util.h>
62
+#include <pulsecore/device-port.h>
63
+#include <pulsecore/i18n.h>
66
+#include "alsa-util.h"
67
+#include "alsa-extcon.h"
69
+/* IFDEF HAVE_UCM ? */
70
+#include <use-case.h>
71
+#include "alsa-ucm.h"
75
+#define EXTCON_NAME "switch"
77
+/* #define EXTCON_NAME "extcon" */
80
+/* TODO: Backport stuff to 4.0, remove before upstreaming */
81
+#ifndef PA_PORT_AVAILABLE_YES
82
+#define PA_PORT_AVAILABLE_YES PA_AVAILABLE_YES
83
+#define PA_PORT_AVAILABLE_NO PA_AVAILABLE_NO
84
+#define PA_PORT_AVAILABLE_UNKNOWN PA_AVAILABLE_UNKNOWN
85
+#define pa_port_available_t pa_available_t
88
+static pa_port_available_t hp_avail(int state)
90
+ return ((state & 3) != 0) ? PA_PORT_AVAILABLE_YES : PA_PORT_AVAILABLE_NO;
93
+static pa_port_available_t hsmic_avail(int state)
95
+ return (state & 1) ? PA_PORT_AVAILABLE_YES : PA_PORT_AVAILABLE_NO;
98
+struct android_switch {
100
+ uint32_t current_value;
103
+static void android_switch_free(struct android_switch *as) {
106
+ pa_xfree(as->name);
110
+static struct android_switch *android_switch_new(const char *name) {
112
+ struct android_switch *as = NULL;
113
+ char *filename = pa_sprintf_malloc("/sys/class/%s/%s/state", EXTCON_NAME, name);
114
+ char *state = pa_read_line_from_file(filename);
116
+ if (state == NULL) {
117
+ pa_log_debug("Cannot open '%s'. Skipping.", filename);
118
+ pa_xfree(filename);
121
+ pa_xfree(filename);
123
+ as = pa_xnew0(struct android_switch, 1);
124
+ as->name = pa_xstrdup(name);
126
+ if (pa_atou(state, &as->current_value) < 0) {
127
+ pa_log_warn("Switch '%s' has invalid value '%s'", name, state);
129
+ android_switch_free(as);
138
+ struct udev_monitor *monitor;
139
+ pa_io_event *event;
142
+struct pa_alsa_extcon {
144
+ struct android_switch *h2w;
145
+ struct udev_data udev;
148
+static struct android_switch *find_matching_switch(pa_alsa_extcon *u,
149
+ const char *devpath) {
151
+ if (pa_streq(devpath, "/devices/virtual/" EXTCON_NAME "/h2w"))
152
+ return u->h2w; /* To be extended if we ever support more switches */
156
+static void notify_ports(pa_alsa_extcon *u, struct android_switch *as) {
161
+ pa_assert(as == u->h2w); /* To be extended if we ever support more switches */
163
+ pa_log_debug("Value of switch %s is now %d.", as->name, as->current_value);
165
+ PA_HASHMAP_FOREACH(p, u->card->ports, state) {
166
+ if (p->is_output) {
167
+ if (!strcmp(p->name, "analog-output-headphones"))
168
+ pa_device_port_set_available(p, hp_avail(as->current_value));
169
+/* IFDEF HAVE_UCM ? */
170
+ else if (pa_alsa_ucm_port_contains(p->name, SND_USE_CASE_DEV_HEADSET, true) ||
171
+ pa_alsa_ucm_port_contains(p->name, SND_USE_CASE_DEV_HEADPHONES, true))
172
+ pa_device_port_set_available(p, hp_avail(as->current_value));
176
+ if (!strcmp(p->name, "analog-input-headset-mic"))
177
+ pa_device_port_set_available(p, hsmic_avail(as->current_value));
178
+/* IFDEF HAVE_UCM ? */
179
+ else if (pa_alsa_ucm_port_contains(p->name, SND_USE_CASE_DEV_HEADSET, false))
180
+ pa_device_port_set_available(p, hsmic_avail(as->current_value));
186
+static void udev_cb(pa_mainloop_api *a, pa_io_event *e, int fd,
187
+ pa_io_event_flags_t events, void *userdata) {
189
+ pa_alsa_extcon *u = userdata;
190
+ struct udev_device *d = udev_monitor_receive_device(u->udev.monitor);
191
+ struct udev_list_entry *entry;
192
+ struct android_switch *as;
193
+ const char *devpath, *state;
196
+ pa_log("udev_monitor_receive_device failed.");
198
+ a->io_free(u->udev.event);
199
+ u->udev.event = NULL;
203
+ devpath = udev_device_get_devpath(d);
205
+ pa_log("udev_device_get_devpath failed.");
208
+ pa_log_debug("Got uevent with devpath=%s", devpath);
210
+ as = find_matching_switch(u, devpath);
214
+ entry = udev_list_entry_get_by_name(
215
+ udev_device_get_properties_list_entry(d), "SWITCH_STATE");
217
+ pa_log("udev_list_entry_get_by_name failed to find 'SWITCH_STATE' entry.");
221
+ state = udev_list_entry_get_value(entry);
223
+ pa_log("udev_list_entry_get_by_name failed.");
227
+ if (pa_atou(state, &as->current_value) < 0) {
228
+ pa_log_warn("Switch '%s' has invalid value '%s'", as->name, state);
232
+ notify_ports(u, as);
235
+ udev_device_unref(d);
238
+static bool init_udev(pa_alsa_extcon *u, pa_core *core) {
242
+ u->udev.udev = udev_new();
243
+ if (!u->udev.udev) {
244
+ pa_log("udev_new failed.");
248
+ u->udev.monitor = udev_monitor_new_from_netlink(u->udev.udev, "udev");
249
+ if (!u->udev.monitor) {
250
+ pa_log("udev_monitor_new_from_netlink failed.");
254
+ if (udev_monitor_filter_add_match_subsystem_devtype(u->udev.monitor, EXTCON_NAME, NULL) < 0) {
255
+ pa_log("udev_monitor_filter_add_match_subsystem_devtype failed.");
259
+ if (udev_monitor_enable_receiving(u->udev.monitor) < 0) {
260
+ pa_log("udev_monitor_enable_receiving failed.");
264
+ fd = udev_monitor_get_fd(u->udev.monitor);
266
+ pa_log("udev_monitor_get_fd failed");
270
+ pa_assert_se(u->udev.event = core->mainloop->io_new(core->mainloop, fd,
271
+ PA_IO_EVENT_INPUT, udev_cb, u));
276
+pa_alsa_extcon *pa_alsa_extcon_new(pa_core *core, pa_card *card) {
278
+ pa_alsa_extcon *u = pa_xnew0(pa_alsa_extcon, 1);
282
+ pa_log_error("pa_alsa_extcon_new start 2");
284
+ u->h2w = android_switch_new("h2w");
288
+ if (!init_udev(u, core))
291
+ notify_ports(u, u->h2w);
292
+ pa_log_error("pa_alsa_extcon_new finish");
296
+ pa_alsa_extcon_free(u);
297
+ pa_log_error("pa_alsa_extcon_new fail");
301
+void pa_alsa_extcon_free(pa_alsa_extcon *u) {
306
+ u->card->core->mainloop->io_free(u->udev.event);
308
+ if (u->udev.monitor)
309
+ udev_monitor_unref(u->udev.monitor);
312
+ udev_unref(u->udev.udev);
315
+ android_switch_free(u->h2w);
319
diff --git a/src/modules/alsa/alsa-extcon.h b/src/modules/alsa/alsa-extcon.h
321
index 0000000..7655746
323
+++ b/src/modules/alsa/alsa-extcon.h
325
+#ifndef fooalsaextconhfoo
326
+#define fooalsaextconhfoo
329
+ This file is part of PulseAudio.
331
+ Copyright 2013 David Henningsson, Canonical Ltd.
333
+ PulseAudio is free software; you can redistribute it and/or modify
334
+ it under the terms of the GNU Lesser General Public License as published
335
+ by the Free Software Foundation; either version 2.1 of the License,
336
+ or (at your option) any later version.
338
+ PulseAudio is distributed in the hope that it will be useful, but
339
+ WITHOUT ANY WARRANTY; without even the implied warranty of
340
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
341
+ General Public License for more details.
343
+ You should have received a copy of the GNU Lesser General Public License
344
+ along with PulseAudio; if not, write to the Free Software
345
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
349
+/* TODO: Handle !HAVE_UDEV and !HAVE_UCM */
351
+typedef struct pa_alsa_extcon pa_alsa_extcon;
353
+pa_alsa_extcon *pa_alsa_extcon_new(pa_core *, pa_card *);
355
+void pa_alsa_extcon_free(pa_alsa_extcon *);
358
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
359
index f69ee89..ae90fec 100644
360
--- a/src/modules/alsa/alsa-ucm.c
361
+++ b/src/modules/alsa/alsa-ucm.c
362
@@ -719,7 +719,7 @@ static void ucm_add_port_combination(
366
-static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
367
+int pa_alsa_ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
370
const char *state = NULL;
371
@@ -973,7 +973,7 @@ int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *p
372
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
373
const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
375
- if (ucm_port_contains(port->name, dev_name, is_sink))
376
+ if (pa_alsa_ucm_port_contains(port->name, dev_name, is_sink))
377
enable_devs[enable_num++] = dev_name;
379
pa_log_debug("Disable ucm device %s", dev_name);
380
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
381
index cdeb469..8caa414 100644
382
--- a/src/modules/alsa/alsa-ucm.h
383
+++ b/src/modules/alsa/alsa-ucm.h
384
@@ -102,6 +102,7 @@ void pa_alsa_ucm_add_ports_combination(
387
int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
388
+int pa_alsa_ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink);
390
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
391
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
392
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
393
index fe05e3d..eb8f4e7 100644
394
--- a/src/modules/alsa/module-alsa-card.c
395
+++ b/src/modules/alsa/module-alsa-card.c
398
#include "alsa-util.h"
399
#include "alsa-ucm.h"
400
+#include "alsa-extcon.h"
401
#include "alsa-sink.h"
402
#include "alsa-source.h"
403
#include "module-alsa-card-symdef.h"
404
@@ -114,6 +115,7 @@ struct userdata {
405
snd_hctl_t *hctl_handle;
407
pa_alsa_fdlist *mixer_fdl;
408
+ pa_alsa_extcon *extcon;
412
@@ -752,6 +754,7 @@ int pa__init(pa_module *m) {
413
u->card->set_profile = card_set_profile;
416
+ u->extcon = pa_alsa_extcon_new(m->core, u->card);
420
@@ -817,6 +820,8 @@ void pa__done(pa_module*m) {
421
if (u->source_output_unlink_hook_slot)
422
pa_hook_slot_free(u->source_output_unlink_hook_slot);
425
+ pa_alsa_extcon_free(u->extcon);
427
pa_alsa_fdlist_free(u->mixer_fdl);