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 
39 PA_MODULE_AUTHOR("Greg V");
40 PA_MODULE_DESCRIPTION("Detect hotplugged audio hardware and load matching drivers");
41 PA_MODULE_VERSION(PACKAGE_VERSION);
42 PA_MODULE_LOAD_ONCE(true);
43 PA_MODULE_USAGE("");
44 
45 struct userdata {
46     pa_core *core;
47     pa_hashmap *devices;
48     pa_iochannel *io;
49     pa_ioline *line;
50 };
51 
line_callback(pa_ioline *line, const char *s, void *userdata)52 static 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 
device_free(void *a)83 static void device_free(void *a) {
84 }
85 
pa__init(pa_module *m)86 int 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 
pa__done(pa_module *m)117 void 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