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
;
88 #if defined(HAVE_ALSA) && defined(HAVE_OSS)
98 static const char* get_capability_name(capability_t cap
) {
101 return capabilities
[cap
];
104 static void hal_device_free(struct device
* d
) {
109 static void hal_device_free_cb(void *d
, PA_GCC_UNUSED
void *data
) {
110 hal_device_free((struct device
*) d
);
113 static const char *strip_udi(const char *udi
) {
115 if ((slash
= strrchr(udi
, '/')))
129 static alsa_type_t
hal_device_get_alsa_type(LibHalContext
*ctx
, const char *udi
,
135 type
= libhal_device_get_property_string(ctx
, udi
, "alsa.type", error
);
136 if (!type
|| dbus_error_is_set(error
))
139 if (!strcmp(type
, "playback")) {
141 } else if (!strcmp(type
, "capture")) {
142 t
= ALSA_TYPE_SOURCE
;
146 libhal_free_string(type
);
151 static int hal_device_get_alsa_card(LibHalContext
*ctx
, const char *udi
,
154 return libhal_device_get_property_int(ctx
, udi
, "alsa.card", error
);
157 static int hal_device_get_alsa_device(LibHalContext
*ctx
, const char *udi
,
160 return libhal_device_get_property_int(ctx
, udi
, "alsa.device", error
);
163 static pa_module
* hal_device_load_alsa(struct userdata
*u
, const char *udi
,
169 const char *module_name
;
171 type
= hal_device_get_alsa_type(u
->ctx
, udi
, error
);
172 if (dbus_error_is_set(error
) || type
== ALSA_TYPE_OTHER
)
175 device
= hal_device_get_alsa_device(u
->ctx
, udi
, error
);
176 if (dbus_error_is_set(error
) || device
!= 0)
179 card
= hal_device_get_alsa_card(u
->ctx
, udi
, error
);
180 if (dbus_error_is_set(error
))
183 if (type
== ALSA_TYPE_SINK
) {
184 module_name
= "module-alsa-sink";
185 snprintf(args
, sizeof(args
), "device=hw:%u sink_name=alsa_output.%s", card
, strip_udi(udi
));
187 module_name
= "module-alsa-source";
188 snprintf(args
, sizeof(args
), "device=hw:%u source_name=alsa_input.%s", card
, strip_udi(udi
));
191 pa_log_debug("Loading %s with arguments '%s'", module_name
, args
);
193 return pa_module_load(u
->core
, module_name
, args
);
199 static dbus_bool_t
hal_device_is_oss_pcm(LibHalContext
*ctx
, const char *udi
,
202 dbus_bool_t rv
= FALSE
;
203 char* type
, *device_file
= NULL
;
206 type
= libhal_device_get_property_string(ctx
, udi
, "oss.type", error
);
207 if (!type
|| dbus_error_is_set(error
))
210 if (!strcmp(type
, "pcm")) {
213 device
= libhal_device_get_property_int(ctx
, udi
, "oss.device", error
);
214 if (dbus_error_is_set(error
) || device
!= 0)
217 device_file
= libhal_device_get_property_string(ctx
, udi
, "oss.device_file",
219 if (!device_file
|| dbus_error_is_set(error
))
222 /* hack to ignore /dev/audio style devices */
223 if ((e
= strrchr(device_file
, '/')))
224 rv
= !pa_startswith(e
+ 1, "audio");
228 libhal_free_string(type
);
229 libhal_free_string(device_file
);
233 static pa_module
* hal_device_load_oss(struct userdata
*u
, const char *udi
,
239 if (!hal_device_is_oss_pcm(u
->ctx
, udi
, error
) || dbus_error_is_set(error
))
242 device
= libhal_device_get_property_string(u
->ctx
, udi
, "oss.device_file",
244 if (!device
|| dbus_error_is_set(error
))
247 snprintf(args
, sizeof(args
), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device
, strip_udi(udi
), strip_udi(udi
));
248 libhal_free_string(device
);
250 pa_log_debug("Loading module-oss with arguments '%s'", args
);
252 return pa_module_load(u
->core
, "module-oss", args
);
256 static dbus_bool_t
hal_device_add(struct userdata
*u
, const char *udi
,
262 switch(u
->capability
) {
265 m
= hal_device_load_alsa(u
, udi
, error
);
273 m
= hal_device_load_oss(u
, udi
, error
);
277 assert(FALSE
); /* never reached */
281 if (!m
|| dbus_error_is_set(error
))
284 d
= pa_xnew(struct device
, 1);
285 d
->udi
= pa_xstrdup(udi
);
288 pa_hashmap_put(u
->devices
, d
->udi
, d
);
293 static int hal_device_add_all(struct userdata
*u
, capability_t capability
)
299 const char* cap
= get_capability_name(capability
);
301 assert(capability
< CAP_MAX
);
303 pa_log_info("Trying capability %u (%s)", capability
, cap
);
304 dbus_error_init(&error
);
305 udis
= libhal_find_device_by_capability(u
->ctx
, cap
, &n
, &error
);
306 if (dbus_error_is_set(&error
)) {
307 pa_log_error("Error finding devices: %s: %s", error
.name
,
309 dbus_error_free(&error
);
313 u
->capability
= capability
;
314 for (i
= 0; i
< n
; ++i
) {
315 r
= hal_device_add(u
, udis
[i
], &error
);
316 if (dbus_error_is_set(&error
)) {
317 pa_log_error("Error adding device: %s: %s", error
.name
,
319 dbus_error_free(&error
);
327 libhal_free_string_array(udis
);
331 static dbus_bool_t
device_has_capability(LibHalContext
*ctx
, const char *udi
,
332 const char* cap
, DBusError
*error
)
334 dbus_bool_t has_prop
;
335 has_prop
= libhal_device_property_exists(ctx
, udi
, "info.capabilities",
337 if (!has_prop
|| dbus_error_is_set(error
))
340 return libhal_device_query_capability(ctx
, udi
, cap
, error
);
343 static void device_added_time_cb(pa_mainloop_api
*ea
, pa_time_event
*ev
,
344 const struct timeval
*tv
, void *userdata
)
347 struct timerdata
*td
= (struct timerdata
*) userdata
;
349 dbus_error_init(&error
);
350 if (libhal_device_exists(td
->u
->ctx
, td
->udi
, &error
))
351 hal_device_add(td
->u
, td
->udi
, &error
);
353 if (dbus_error_is_set(&error
)) {
354 pa_log_error("Error adding device: %s: %s", error
.name
,
356 dbus_error_free(&error
);
364 static void device_added_cb(LibHalContext
*ctx
, const char *udi
)
370 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
371 const char* cap
= get_capability_name(u
->capability
);
373 pa_log_debug("HAL Device added: %s", udi
);
375 dbus_error_init(&error
);
376 has_cap
= device_has_capability(ctx
, udi
, cap
, &error
);
377 if (dbus_error_is_set(&error
)) {
378 pa_log_error("Error getting capability: %s: %s", error
.name
,
380 dbus_error_free(&error
);
388 /* actually add the device 1/2 second later */
389 t
= pa_xnew(struct timerdata
, 1);
391 t
->udi
= pa_xstrdup(udi
);
393 pa_gettimeofday(&tv
);
394 pa_timeval_add(&tv
, 500000);
395 u
->core
->mainloop
->time_new(u
->core
->mainloop
, &tv
,
396 device_added_time_cb
, t
);
399 static void device_removed_cb(LibHalContext
* ctx
, const char *udi
)
402 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
404 pa_log_debug("Device removed: %s", udi
);
405 if ((d
= pa_hashmap_remove(u
->devices
, udi
))) {
406 pa_module_unload_by_index(u
->core
, d
->index
);
411 static void new_capability_cb(LibHalContext
*ctx
, const char *udi
,
412 const char* capability
)
414 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
415 const char* capname
= get_capability_name(u
->capability
);
417 if (capname
&& !strcmp(capname
, capability
)) {
418 /* capability we care about, pretend it's a new device */
419 device_added_cb(ctx
, udi
);
423 static void lost_capability_cb(LibHalContext
*ctx
, const char *udi
,
424 const char* capability
)
426 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
427 const char* capname
= get_capability_name(u
->capability
);
429 if (capname
&& !strcmp(capname
, capability
)) {
430 /* capability we care about, pretend it was removed */
431 device_removed_cb(ctx
, udi
);
436 static void property_modified_cb(LibHalContext
*ctx
, const char *udi
,
438 dbus_bool_t is_removed
,
439 dbus_bool_t is_added
)
444 static void pa_hal_context_free(LibHalContext
* hal_ctx
)
448 dbus_error_init(&error
);
449 libhal_ctx_shutdown(hal_ctx
, &error
);
450 libhal_ctx_free(hal_ctx
);
452 if (dbus_error_is_set(&error
)) {
453 dbus_error_free(&error
);
457 static void userdata_free(struct userdata
*u
) {
458 pa_hal_context_free(u
->ctx
);
459 /* free the devices with the hashmap */
460 pa_hashmap_free(u
->devices
, hal_device_free_cb
, NULL
);
461 pa_dbus_connection_unref(u
->conn
);
465 static LibHalContext
* pa_hal_context_new(pa_core
* c
, DBusConnection
*conn
)
468 LibHalContext
*hal_ctx
= NULL
;
470 dbus_error_init(&error
);
471 if (!(hal_ctx
= libhal_ctx_new())) {
472 pa_log_error("libhal_ctx_new() failed");
476 if (!libhal_ctx_set_dbus_connection(hal_ctx
, conn
)) {
477 pa_log_error("Error establishing DBUS connection: %s: %s",
478 error
.name
, error
.message
);
482 if (!libhal_ctx_init(hal_ctx
, &error
)) {
483 pa_log_error("Couldn't connect to hald: %s: %s",
484 error
.name
, error
.message
);
492 pa_hal_context_free(hal_ctx
);
494 if (dbus_error_is_set(&error
))
495 dbus_error_free(&error
);
500 int pa__init(pa_core
*c
, pa_module
*m
) {
502 pa_dbus_connection
*conn
;
503 struct userdata
*u
= NULL
;
504 LibHalContext
*hal_ctx
= NULL
;
510 dbus_error_init(&error
);
511 if (!(conn
= pa_dbus_bus_get(c
, DBUS_BUS_SYSTEM
, &error
))) {
512 pa_log_error("Unable to contact DBUS system bus: %s: %s",
513 error
.name
, error
.message
);
514 dbus_error_free(&error
);
518 if (!(hal_ctx
= pa_hal_context_new(c
, pa_dbus_connection_get(conn
)))) {
519 /* pa_hal_context_new() logs appropriate errors */
523 u
= pa_xnew(struct userdata
, 1);
527 u
->devices
= pa_hashmap_new(pa_idxset_string_hash_func
,
528 pa_idxset_string_compare_func
);
529 m
->userdata
= (void*) u
;
532 n
= hal_device_add_all(u
, CAP_ALSA
);
534 #if defined(HAVE_ALSA) && defined(HAVE_OSS)
540 n
+= hal_device_add_all(u
, CAP_OSS
);
542 #if defined(HAVE_ALSA) && defined(HAVE_OSS)
544 /* We found something with OSS, but didn't find anything with
545 * ALSA. Then let's use only OSS from now on. */
551 libhal_ctx_set_user_data(hal_ctx
, u
);
552 libhal_ctx_set_device_added(hal_ctx
, device_added_cb
);
553 libhal_ctx_set_device_removed(hal_ctx
, device_removed_cb
);
554 libhal_ctx_set_device_new_capability(hal_ctx
, new_capability_cb
);
555 libhal_ctx_set_device_lost_capability(hal_ctx
, lost_capability_cb
);
556 /*libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);*/
558 dbus_error_init(&error
);
559 if (!libhal_device_property_watch_all(hal_ctx
, &error
)) {
560 pa_log_error("error monitoring device list: %s: %s",
561 error
.name
, error
.message
);
562 dbus_error_free(&error
);
567 pa_log_info("loaded %i modules.", n
);
573 void pa__done(PA_GCC_UNUSED pa_core
*c
, pa_module
*m
) {
576 /* free the user data */
577 userdata_free(m
->userdata
);