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