4 This file is part of PulseAudio.
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Shams E. King
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
36 #include <sys/types.h>
39 #include <pulse/xmalloc.h>
40 #include <pulse/timeval.h>
42 #include <pulsecore/core-error.h>
43 #include <pulsecore/module.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/hashmap.h>
46 #include <pulsecore/idxset.h>
47 #include <pulsecore/core-util.h>
49 #include <hal/libhal.h>
51 #include "dbus-util.h"
52 #include "module-hal-detect-symdef.h"
54 PA_MODULE_AUTHOR("Shahms King")
55 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
56 PA_MODULE_VERSION(PACKAGE_VERSION)
68 static const char* const capabilities[CAP_MAX] = {
85 capability_t capability;
86 pa_dbus_connection *conn;
95 static const char* get_capability_name(capability_t cap) {
98 return capabilities[cap];
101 static void hal_device_free(struct device* d) {
106 static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
107 hal_device_free((struct device*) d);
110 static const char *strip_udi(const char *udi) {
112 if ((slash = strrchr(udi, '/')))
126 static alsa_type_t hal_device_get_alsa_type(LibHalContext *ctx, const char *udi,
132 type = libhal_device_get_property_string(ctx, udi, "alsa.type", error);
133 if (!type || dbus_error_is_set(error))
136 if (!strcmp(type, "playback")) {
138 } else if (!strcmp(type, "capture")) {
139 t = ALSA_TYPE_SOURCE;
143 libhal_free_string(type);
148 static int hal_device_get_alsa_card(LibHalContext *ctx, const char *udi,
151 return libhal_device_get_property_int(ctx, udi, "alsa.card", error);
154 static int hal_device_get_alsa_device(LibHalContext *ctx, const char *udi,
157 return libhal_device_get_property_int(ctx, udi, "alsa.device", error);
160 static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi,
166 const char *module_name;
168 type = hal_device_get_alsa_type(u->ctx, udi, error);
169 if (dbus_error_is_set(error) || type == ALSA_TYPE_OTHER)
172 device = hal_device_get_alsa_device(u->ctx, udi, error);
173 if (dbus_error_is_set(error) || device != 0)
176 card = hal_device_get_alsa_card(u->ctx, udi, error);
177 if (dbus_error_is_set(error))
180 if (type == ALSA_TYPE_SINK) {
181 module_name = "module-alsa-sink";
182 snprintf(args, sizeof(args), "device=hw:%u sink_name=alsa_output.%s", card, strip_udi(udi));
184 module_name = "module-alsa-source";
185 snprintf(args, sizeof(args), "device=hw:%u source_name=alsa_input.%s", card, strip_udi(udi));
188 return pa_module_load(u->core, module_name, args);
194 static dbus_bool_t hal_device_is_oss_pcm(LibHalContext *ctx, const char *udi,
197 dbus_bool_t rv = FALSE;
198 char* type, *device_file = NULL;
201 type = libhal_device_get_property_string(ctx, udi, "oss.type", error);
202 if (!type || dbus_error_is_set(error))
205 if (!strcmp(type, "pcm")) {
208 device = libhal_device_get_property_int(ctx, udi, "oss.device", error);
209 if (dbus_error_is_set(error) || device != 0)
212 device_file = libhal_device_get_property_string(ctx, udi, "oss.device_file",
214 if (!device_file || dbus_error_is_set(error))
217 /* hack to ignore /dev/audio style devices */
218 if ((e = strrchr(device_file, '/')))
219 rv = !pa_startswith(e + 1, "audio");
223 libhal_free_string(type);
224 libhal_free_string(device_file);
228 static pa_module* hal_device_load_oss(struct userdata *u, const char *udi,
234 if (!hal_device_is_oss_pcm(u->ctx, udi, error) || dbus_error_is_set(error))
237 device = libhal_device_get_property_string(u->ctx, udi, "oss.device_file",
239 if (!device || dbus_error_is_set(error))
242 snprintf(args, sizeof(args), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device, strip_udi(udi), strip_udi(udi));
243 libhal_free_string(device);
245 return pa_module_load(u->core, "module-oss", args);
249 static dbus_bool_t hal_device_add(struct userdata *u, const char *udi,
255 switch(u->capability) {
258 m = hal_device_load_alsa(u, udi, error);
263 m = hal_device_load_oss(u, udi, error);
267 assert(FALSE); /* never reached */
271 if (!m || dbus_error_is_set(error))
274 d = pa_xnew(struct device, 1);
275 d->udi = pa_xstrdup(udi);
278 pa_hashmap_put(u->devices, d->udi, d);
283 static int hal_device_add_all(struct userdata *u, capability_t capability)
289 const char* cap = get_capability_name(capability);
291 assert(capability < CAP_MAX);
293 pa_log_info("Trying capability %u (%s)", capability, cap);
294 dbus_error_init(&error);
295 udis = libhal_find_device_by_capability(u->ctx, cap, &n, &error);
296 if (dbus_error_is_set(&error)) {
297 pa_log_error("Error finding devices: %s: %s", error.name,
299 dbus_error_free(&error);
303 u->capability = capability;
304 for (i = 0; i < n; ++i) {
305 r = hal_device_add(u, udis[i], &error);
306 if (dbus_error_is_set(&error)) {
307 pa_log_error("Error adding device: %s: %s", error.name,
309 dbus_error_free(&error);
317 libhal_free_string_array(udis);
321 static dbus_bool_t device_has_capability(LibHalContext *ctx, const char *udi,
322 const char* cap, DBusError *error)
324 dbus_bool_t has_prop;
325 has_prop = libhal_device_property_exists(ctx, udi, "info.capabilities",
327 if (!has_prop || dbus_error_is_set(error))
330 return libhal_device_query_capability(ctx, udi, cap, error);
333 static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev,
334 const struct timeval *tv, void *userdata)
337 struct timerdata *td = (struct timerdata*) userdata;
339 dbus_error_init(&error);
340 if (libhal_device_exists(td->u->ctx, td->udi, &error))
341 hal_device_add(td->u, td->udi, &error);
343 if (dbus_error_is_set(&error)) {
344 pa_log_error("Error adding device: %s: %s", error.name,
346 dbus_error_free(&error);
354 static void device_added_cb(LibHalContext *ctx, const char *udi)
360 struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
361 const char* cap = get_capability_name(u->capability);
363 pa_log_debug("HAL Device added: %s", udi);
365 dbus_error_init(&error);
366 has_cap = device_has_capability(ctx, udi, cap, &error);
367 if (dbus_error_is_set(&error)) {
368 pa_log_error("Error getting capability: %s: %s", error.name,
370 dbus_error_free(&error);
378 /* actually add the device 1/2 second later */
379 t = pa_xnew(struct timerdata, 1);
381 t->udi = pa_xstrdup(udi);
383 pa_gettimeofday(&tv);
384 pa_timeval_add(&tv, 500000);
385 u->core->mainloop->time_new(u->core->mainloop, &tv,
386 device_added_time_cb, t);
389 static void device_removed_cb(LibHalContext* ctx, const char *udi)
392 struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
394 pa_log_debug("Device removed: %s", udi);
395 if ((d = pa_hashmap_remove(u->devices, udi))) {
396 pa_module_unload_by_index(u->core, d->index);
401 static void new_capability_cb(LibHalContext *ctx, const char *udi,
402 const char* capability)
404 struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
405 const char* capname = get_capability_name(u->capability);
407 if (capname && !strcmp(capname, capability)) {
408 /* capability we care about, pretend it's a new device */
409 device_added_cb(ctx, udi);
413 static void lost_capability_cb(LibHalContext *ctx, const char *udi,
414 const char* capability)
416 struct userdata *u = (struct userdata*) libhal_ctx_get_user_data(ctx);
417 const char* capname = get_capability_name(u->capability);
419 if (capname && !strcmp(capname, capability)) {
420 /* capability we care about, pretend it was removed */
421 device_removed_cb(ctx, udi);
426 static void property_modified_cb(LibHalContext *ctx, const char *udi,
428 dbus_bool_t is_removed,
429 dbus_bool_t is_added)
434 static void pa_hal_context_free(LibHalContext* hal_ctx)
438 dbus_error_init(&error);
439 libhal_ctx_shutdown(hal_ctx, &error);
440 libhal_ctx_free(hal_ctx);
442 if (dbus_error_is_set(&error)) {
443 dbus_error_free(&error);
447 static void userdata_free(struct userdata *u) {
448 pa_hal_context_free(u->ctx);
449 /* free the devices with the hashmap */
450 pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
451 pa_dbus_connection_unref(u->conn);
455 static LibHalContext* pa_hal_context_new(pa_core* c, DBusConnection *conn)
458 LibHalContext *hal_ctx = NULL;
460 dbus_error_init(&error);
461 if (!(hal_ctx = libhal_ctx_new())) {
462 pa_log_error("libhal_ctx_new() failed");
466 if (!libhal_ctx_set_dbus_connection(hal_ctx, conn)) {
467 pa_log_error("Error establishing DBUS connection: %s: %s",
468 error.name, error.message);
472 if (!libhal_ctx_init(hal_ctx, &error)) {
473 pa_log_error("Couldn't connect to hald: %s: %s",
474 error.name, error.message);
482 pa_hal_context_free(hal_ctx);
484 if (dbus_error_is_set(&error))
485 dbus_error_free(&error);
490 int pa__init(pa_core *c, pa_module*m) {
493 pa_dbus_connection *conn;
494 struct userdata *u = NULL;
495 LibHalContext *hal_ctx = NULL;
500 dbus_error_init(&error);
501 if (!(conn = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &error))) {
502 pa_log_error("Unable to contact DBUS system bus: %s: %s",
503 error.name, error.message);
504 dbus_error_free(&error);
508 if (!(hal_ctx = pa_hal_context_new(c, pa_dbus_connection_get(conn)))) {
509 /* pa_hal_context_new() logs appropriate errors */
513 u = pa_xnew(struct userdata, 1);
517 u->devices = pa_hashmap_new(pa_idxset_string_hash_func,
518 pa_idxset_string_compare_func);
519 m->userdata = (void*) u;
522 if ((n = hal_device_add_all(u, CAP_ALSA)) <= 0)
525 if ((n = hal_device_add_all(u, CAP_OSS)) <= 0)
528 pa_log_warn("failed to detect any sound hardware.");
533 libhal_ctx_set_user_data(hal_ctx, (void*) u);
534 libhal_ctx_set_device_added(hal_ctx, device_added_cb);
535 libhal_ctx_set_device_removed(hal_ctx, device_removed_cb);
536 libhal_ctx_set_device_new_capability(hal_ctx, new_capability_cb);
537 libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability_cb);
538 /*libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);*/
540 dbus_error_init(&error);
541 if (!libhal_device_property_watch_all(hal_ctx, &error)) {
542 pa_log_error("error monitoring device list: %s: %s",
543 error.name, error.message);
544 dbus_error_free(&error);
549 pa_log_info("loaded %i modules.", n);
555 void pa__done(PA_GCC_UNUSED pa_core *c, pa_module *m) {
558 /* free the user data */
559 userdata_free(m->userdata);