From 72792db74c63e6c16cda5b39694cdf7420f9f57e Mon Sep 17 00:00:00 2001 From: Greg V Date: Fri, 3 Apr 2020 15:37:24 +0300 Subject: [PATCH] module-devd-detect: new module for soundcard hotplug on FreeBSD Used in addition to module-detect, since devd only provides hotplug events. Part-of: --- meson.build | 2 + src/daemon/default.pa.in | 6 ++ src/daemon/system.pa.in | 6 ++ src/modules/meson.build | 4 + src/modules/module-devd-detect.c | 135 +++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 src/modules/module-devd-detect.c diff --git a/meson.build b/meson.build index 974161930..a910578ca 100644 --- a/meson.build +++ b/meson.build @@ -187,6 +187,8 @@ elif host_machine.system() == 'windows' pcreposix_dep = meson.get_compiler('c').find_library('pcreposix') platform_socket_dep = [ws2_32_dep, winsock_dep] platform_dep = [ole32_dep, ssp_dep, pcreposix_dep] +elif host_machine.system() == 'freebsd' + cdata.set('OS_IS_FREEBSD', 1) #elif host_machine.system() == 'solaris' # # Apparently meson has no solaris support? # # Needed to get declarations for msg_control and msg_controllen on Solaris diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index 3468a89b9..1d6b64cb8 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -67,6 +67,12 @@ load-module module-coreaudio-detect ### Use the static hardware detection module (for systems that lack udev support) load-module module-detect .endif +ifelse(@OS_IS_FREEBSD@, 1, [dnl +### FreeBSD devd is used in addition to static detection (only handles hotplug) +.ifexists module-devd-detect@PA_SOEXT@ +load-module module-devd-detect +.endif +])dnl ### Automatically connect sink and source if JACK server is present .ifexists module-jackdbus-detect@PA_SOEXT@ diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index 1470e2368..989fcb4f3 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -39,6 +39,12 @@ load-module module-coreaudio-detect ### Use the static hardware detection module (for systems that lack udev/hal support) load-module module-detect .endif +ifelse(@OS_IS_FREEBSD@, 1, [dnl +### FreeBSD devd is used in addition to static detection (only handles hotplug) +.ifexists module-devd-detect@PA_SOEXT@ +load-module module-devd-detect +.endif +])dnl ### Load several protocols .ifexists module-esound-protocol-unix@PA_SOEXT@ diff --git a/src/modules/meson.build b/src/modules/meson.build index dcfc432d8..bda48cc27 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -217,6 +217,10 @@ if udev_dep.found() endif endif +if host_machine.system() == 'freebsd' + all_modules += [ [ 'module-devd-detect', 'module-devd-detect.c', [], [], [] ] ] +endif + if x11_dep.found() all_modules += [ [ 'module-x11-bell', 'x11/module-x11-bell.c', [], [], [x11_dep] ], diff --git a/src/modules/module-devd-detect.c b/src/modules/module-devd-detect.c new file mode 100644 index 000000000..db3c6453b --- /dev/null +++ b/src/modules/module-devd-detect.c @@ -0,0 +1,135 @@ +/*** + This file is part of PulseAudio. + + Copyright 2020 Greg V + + 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, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +PA_MODULE_AUTHOR("Greg V"); +PA_MODULE_DESCRIPTION("Detect hotplugged audio hardware and load matching drivers"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); +PA_MODULE_USAGE(""); + +struct userdata { + pa_core *core; + pa_hashmap *devices; + pa_iochannel *io; + pa_ioline *line; +}; + +static void line_callback(pa_ioline *line, const char *s, void *userdata) { + struct userdata *u = userdata; + pa_module *m = NULL; + unsigned devnum; + uint32_t modidx; + char args[64]; + + pa_assert(line); + pa_assert(u); + + if (sscanf(s, "+pcm%u", &devnum) == 1) { + pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", devnum); + pa_module_load(&m, u->core, "module-oss", args); + + if (m) { + pa_hashmap_put(u->devices, (void *)(uintptr_t)devnum, (void *)(uintptr_t)m->index); + pa_log_info("Card %u module loaded (%u).", devnum, m->index); + } else { + pa_log_info("Card %u failed to load module.", devnum); + } + } else if (sscanf(s, "-pcm%u", &devnum) == 1) { + if (!(modidx = (uint32_t)pa_hashmap_remove(u->devices, (void *)(uintptr_t)devnum))) + return; + + pa_log_info("Card %u (module %u) removed.", devnum, modidx); + + if (modidx != PA_INVALID_INDEX) + pa_module_unload_request_by_index(u->core, modidx, true); + } +} + +static void device_free(void *a) { +} + +int pa__init(pa_module *m) { + struct userdata *u = NULL; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + int fd; + + pa_assert(m); + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->devices = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_free); + + if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0) { + pa_log("Failed to open socket for devd."); + return -1; + } + + strncpy(addr.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(addr.sun_path) - 1); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + pa_log("Failed to connect to devd."); + close(fd); + return -1; + } + + pa_assert_se(u->io = pa_iochannel_new(m->core->mainloop, fd, -1)); + pa_assert_se(u->line = pa_ioline_new(u->io)); + pa_ioline_set_callback(u->line, line_callback, m->userdata); + + return 0; +} + +void pa__done(pa_module *m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->devices) + pa_hashmap_free(u->devices); + + if (u->line) + pa_ioline_close(u->line); + + if (u->io) + pa_iochannel_free(u->io); + + pa_xfree(u); +}