1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2020 Greg V 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdio.h> 25#include <stdint.h> 26#include <stdbool.h> 27#include <unistd.h> 28#include <string.h> 29#include <sys/socket.h> 30#include <sys/un.h> 31 32#include <pulsecore/core-util.h> 33#include <pulsecore/module.h> 34#include <pulsecore/hashmap.h> 35#include <pulsecore/iochannel.h> 36#include <pulsecore/ioline.h> 37#include <pulsecore/log.h> 38 39PA_MODULE_AUTHOR("Greg V"); 40PA_MODULE_DESCRIPTION("Detect hotplugged audio hardware and load matching drivers"); 41PA_MODULE_VERSION(PACKAGE_VERSION); 42PA_MODULE_LOAD_ONCE(true); 43PA_MODULE_USAGE(""); 44 45struct userdata { 46 pa_core *core; 47 pa_hashmap *devices; 48 pa_iochannel *io; 49 pa_ioline *line; 50}; 51 52static void line_callback(pa_ioline *line, const char *s, void *userdata) { 53 struct userdata *u = userdata; 54 pa_module *m = NULL; 55 unsigned devnum; 56 uint32_t modidx; 57 char args[64]; 58 59 pa_assert(line); 60 pa_assert(u); 61 62 if (sscanf(s, "+pcm%u", &devnum) == 1) { 63 pa_snprintf(args, sizeof(args), "device=/dev/dsp%u", devnum); 64 pa_module_load(&m, u->core, "module-oss", args); 65 66 if (m) { 67 pa_hashmap_put(u->devices, (void *)(uintptr_t)devnum, (void *)(uintptr_t)m->index); 68 pa_log_info("Card %u module loaded (%u).", devnum, m->index); 69 } else { 70 pa_log_info("Card %u failed to load module.", devnum); 71 } 72 } else if (sscanf(s, "-pcm%u", &devnum) == 1) { 73 if (!(modidx = (uint32_t)pa_hashmap_remove(u->devices, (void *)(uintptr_t)devnum))) 74 return; 75 76 pa_log_info("Card %u (module %u) removed.", devnum, modidx); 77 78 if (modidx != PA_INVALID_INDEX) 79 pa_module_unload_request_by_index(u->core, modidx, true); 80 } 81} 82 83static void device_free(void *a) { 84} 85 86int pa__init(pa_module *m) { 87 struct userdata *u = NULL; 88 struct sockaddr_un addr = { .sun_family = AF_UNIX }; 89 int fd; 90 91 pa_assert(m); 92 93 m->userdata = u = pa_xnew0(struct userdata, 1); 94 u->core = m->core; 95 u->devices = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_free); 96 97 if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0) { 98 pa_log("Failed to open socket for devd."); 99 return -1; 100 } 101 102 strncpy(addr.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(addr.sun_path) - 1); 103 104 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 105 pa_log("Failed to connect to devd."); 106 close(fd); 107 return -1; 108 } 109 110 pa_assert_se(u->io = pa_iochannel_new(m->core->mainloop, fd, -1)); 111 pa_assert_se(u->line = pa_ioline_new(u->io)); 112 pa_ioline_set_callback(u->line, line_callback, m->userdata); 113 114 return 0; 115} 116 117void pa__done(pa_module *m) { 118 struct userdata *u; 119 120 pa_assert(m); 121 122 if (!(u = m->userdata)) 123 return; 124 125 if (u->devices) 126 pa_hashmap_free(u->devices); 127 128 if (u->line) 129 pa_ioline_close(u->line); 130 131 if (u->io) 132 pa_iochannel_free(u->io); 133 134 pa_xfree(u); 135} 136