153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2020 Greg V
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stdio.h>
2553a5a1b3Sopenharmony_ci#include <stdint.h>
2653a5a1b3Sopenharmony_ci#include <stdbool.h>
2753a5a1b3Sopenharmony_ci#include <unistd.h>
2853a5a1b3Sopenharmony_ci#include <string.h>
2953a5a1b3Sopenharmony_ci#include <sys/socket.h>
3053a5a1b3Sopenharmony_ci#include <sys/un.h>
3153a5a1b3Sopenharmony_ci
3253a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3353a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/hashmap.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/iochannel.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/ioline.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3853a5a1b3Sopenharmony_ci
3953a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Greg V");
4053a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Detect hotplugged audio hardware and load matching drivers");
4153a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
4253a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true);
4353a5a1b3Sopenharmony_ciPA_MODULE_USAGE("");
4453a5a1b3Sopenharmony_ci
4553a5a1b3Sopenharmony_cistruct userdata {
4653a5a1b3Sopenharmony_ci    pa_core *core;
4753a5a1b3Sopenharmony_ci    pa_hashmap *devices;
4853a5a1b3Sopenharmony_ci    pa_iochannel *io;
4953a5a1b3Sopenharmony_ci    pa_ioline *line;
5053a5a1b3Sopenharmony_ci};
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_cistatic void line_callback(pa_ioline *line, const char *s, void *userdata) {
5353a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
5453a5a1b3Sopenharmony_ci    pa_module *m = NULL;
5553a5a1b3Sopenharmony_ci    unsigned devnum;
5653a5a1b3Sopenharmony_ci    uint32_t modidx;
5753a5a1b3Sopenharmony_ci    char args[64];
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ci    pa_assert(line);
6053a5a1b3Sopenharmony_ci    pa_assert(u);
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci    if (sscanf(s, "+pcm%u", &devnum) == 1) {
6353a5a1b3Sopenharmony_ci        pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", devnum);
6453a5a1b3Sopenharmony_ci        pa_module_load(&m, u->core, "module-oss", args);
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_ci        if (m) {
6753a5a1b3Sopenharmony_ci            pa_hashmap_put(u->devices, (void *)(uintptr_t)devnum, (void *)(uintptr_t)m->index);
6853a5a1b3Sopenharmony_ci            pa_log_info("Card %u module loaded (%u).", devnum, m->index);
6953a5a1b3Sopenharmony_ci        } else {
7053a5a1b3Sopenharmony_ci            pa_log_info("Card %u failed to load module.", devnum);
7153a5a1b3Sopenharmony_ci        }
7253a5a1b3Sopenharmony_ci    } else if (sscanf(s, "-pcm%u", &devnum) == 1) {
7353a5a1b3Sopenharmony_ci        if (!(modidx = (uint32_t)pa_hashmap_remove(u->devices, (void *)(uintptr_t)devnum)))
7453a5a1b3Sopenharmony_ci            return;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci        pa_log_info("Card %u (module %u) removed.", devnum, modidx);
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci        if (modidx != PA_INVALID_INDEX)
7953a5a1b3Sopenharmony_ci            pa_module_unload_request_by_index(u->core, modidx, true);
8053a5a1b3Sopenharmony_ci    }
8153a5a1b3Sopenharmony_ci}
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_cistatic void device_free(void *a) {
8453a5a1b3Sopenharmony_ci}
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ciint pa__init(pa_module *m) {
8753a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
8853a5a1b3Sopenharmony_ci    struct sockaddr_un addr = { .sun_family = AF_UNIX };
8953a5a1b3Sopenharmony_ci    int fd;
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci    pa_assert(m);
9253a5a1b3Sopenharmony_ci
9353a5a1b3Sopenharmony_ci    m->userdata = u = pa_xnew0(struct userdata, 1);
9453a5a1b3Sopenharmony_ci    u->core = m->core;
9553a5a1b3Sopenharmony_ci    u->devices = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_free);
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_ci    if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0) {
9853a5a1b3Sopenharmony_ci        pa_log("Failed to open socket for devd.");
9953a5a1b3Sopenharmony_ci        return -1;
10053a5a1b3Sopenharmony_ci    }
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci    strncpy(addr.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(addr.sun_path) - 1);
10353a5a1b3Sopenharmony_ci
10453a5a1b3Sopenharmony_ci    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
10553a5a1b3Sopenharmony_ci        pa_log("Failed to connect to devd.");
10653a5a1b3Sopenharmony_ci        close(fd);
10753a5a1b3Sopenharmony_ci        return -1;
10853a5a1b3Sopenharmony_ci    }
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci    pa_assert_se(u->io = pa_iochannel_new(m->core->mainloop, fd, -1));
11153a5a1b3Sopenharmony_ci    pa_assert_se(u->line = pa_ioline_new(u->io));
11253a5a1b3Sopenharmony_ci    pa_ioline_set_callback(u->line, line_callback, m->userdata);
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_ci    return 0;
11553a5a1b3Sopenharmony_ci}
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_civoid pa__done(pa_module *m) {
11853a5a1b3Sopenharmony_ci    struct userdata *u;
11953a5a1b3Sopenharmony_ci
12053a5a1b3Sopenharmony_ci    pa_assert(m);
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
12353a5a1b3Sopenharmony_ci        return;
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    if (u->devices)
12653a5a1b3Sopenharmony_ci        pa_hashmap_free(u->devices);
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_ci    if (u->line)
12953a5a1b3Sopenharmony_ci        pa_ioline_close(u->line);
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_ci    if (u->io)
13253a5a1b3Sopenharmony_ci        pa_iochannel_free(u->io);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    pa_xfree(u);
13553a5a1b3Sopenharmony_ci}
136