4 This file is part of PulseAudio.
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 of the License,
9 or (at your option) any later version.
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.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
33 #include <sys/types.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/log.h>
42 #include <pulsecore/hashmap.h>
43 #include <pulsecore/idxset.h>
44 #include <pulsecore/core-util.h>
46 #include <hal/libhal.h>
48 #include "dbus-util.h"
49 #include "module-hal-detect-symdef.h"
51 PA_MODULE_AUTHOR("Shahms King")
52 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
53 PA_MODULE_VERSION(PACKAGE_VERSION
)
65 static const char* const capabilities
[CAP_MAX
] = {
82 capability_t capability
;
83 pa_dbus_connection
*conn
;
92 static const char* get_capability_name(capability_t cap
) {
95 return capabilities
[cap
];
98 static void hal_device_free(struct device
* d
) {
103 static void hal_device_free_cb(void *d
, PA_GCC_UNUSED
void *data
) {
104 hal_device_free((struct device
*) d
);
107 static const char *strip_udi(const char *udi
) {
109 if ((slash
= strrchr(udi
, '/')))
123 static alsa_type_t
hal_device_get_alsa_type(LibHalContext
*ctx
, const char *udi
,
129 type
= libhal_device_get_property_string(ctx
, udi
, "alsa.type", error
);
130 if (!type
|| dbus_error_is_set(error
))
133 if (!strcmp(type
, "playback")) {
135 } else if (!strcmp(type
, "capture")) {
136 t
= ALSA_TYPE_SOURCE
;
140 libhal_free_string(type
);
145 static int hal_device_get_alsa_card(LibHalContext
*ctx
, const char *udi
,
148 return libhal_device_get_property_int(ctx
, udi
, "alsa.card", error
);
151 static int hal_device_get_alsa_device(LibHalContext
*ctx
, const char *udi
,
154 return libhal_device_get_property_int(ctx
, udi
, "alsa.device", error
);
157 static pa_module
* hal_device_load_alsa(struct userdata
*u
, const char *udi
,
163 const char *module_name
;
165 type
= hal_device_get_alsa_type(u
->ctx
, udi
, error
);
166 if (dbus_error_is_set(error
) || type
== ALSA_TYPE_OTHER
)
169 device
= hal_device_get_alsa_device(u
->ctx
, udi
, error
);
170 if (dbus_error_is_set(error
) || device
!= 0)
173 card
= hal_device_get_alsa_card(u
->ctx
, udi
, error
);
174 if (dbus_error_is_set(error
))
177 if (type
== ALSA_TYPE_SINK
) {
178 module_name
= "module-alsa-sink";
179 snprintf(args
, sizeof(args
), "device=hw:%u sink_name=alsa_output.%s", card
, strip_udi(udi
));
181 module_name
= "module-alsa-source";
182 snprintf(args
, sizeof(args
), "device=hw:%u source_name=alsa_input.%s", card
, strip_udi(udi
));
185 return pa_module_load(u
->core
, module_name
, args
);
191 static dbus_bool_t
hal_device_is_oss_pcm(LibHalContext
*ctx
, const char *udi
,
194 dbus_bool_t rv
= FALSE
;
195 char* type
, *device_file
= NULL
;
198 type
= libhal_device_get_property_string(ctx
, udi
, "oss.type", error
);
199 if (!type
|| dbus_error_is_set(error
))
202 if (!strcmp(type
, "pcm")) {
205 device
= libhal_device_get_property_int(ctx
, udi
, "oss.device", error
);
206 if (dbus_error_is_set(error
) || device
!= 0)
209 device_file
= libhal_device_get_property_string(ctx
, udi
, "oss.device_file",
211 if (!device_file
|| dbus_error_is_set(error
))
214 /* hack to ignore /dev/audio style devices */
215 if ((e
= strrchr(device_file
, '/')))
216 rv
= !pa_startswith(e
+ 1, "audio");
220 libhal_free_string(type
);
221 libhal_free_string(device_file
);
225 static pa_module
* hal_device_load_oss(struct userdata
*u
, const char *udi
,
231 if (!hal_device_is_oss_pcm(u
->ctx
, udi
, error
) || dbus_error_is_set(error
))
234 device
= libhal_device_get_property_string(u
->ctx
, udi
, "oss.device_file",
236 if (!device
|| dbus_error_is_set(error
))
239 snprintf(args
, sizeof(args
), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device
, strip_udi(udi
), strip_udi(udi
));
240 libhal_free_string(device
);
242 return pa_module_load(u
->core
, "module-oss", args
);
246 static dbus_bool_t
hal_device_add(struct userdata
*u
, const char *udi
,
252 switch(u
->capability
) {
255 m
= hal_device_load_alsa(u
, udi
, error
);
260 m
= hal_device_load_oss(u
, udi
, error
);
264 assert(FALSE
); /* never reached */
268 if (!m
|| dbus_error_is_set(error
))
271 d
= pa_xnew(struct device
, 1);
272 d
->udi
= pa_xstrdup(udi
);
275 pa_hashmap_put(u
->devices
, d
->udi
, d
);
280 static int hal_device_add_all(struct userdata
*u
, capability_t capability
)
286 const char* cap
= get_capability_name(capability
);
288 assert(capability
< CAP_MAX
);
290 pa_log_info(__FILE__
": Trying capability %u (%s)", capability
, cap
);
291 dbus_error_init(&error
);
292 udis
= libhal_find_device_by_capability(u
->ctx
, cap
, &n
, &error
);
293 if (dbus_error_is_set(&error
)) {
294 pa_log_error(__FILE__
": Error finding devices: %s: %s", error
.name
,
296 dbus_error_free(&error
);
300 u
->capability
= capability
;
301 for (i
= 0; i
< n
; ++i
) {
302 r
= hal_device_add(u
, udis
[i
], &error
);
303 if (dbus_error_is_set(&error
)) {
304 pa_log_error(__FILE__
": Error adding device: %s: %s", error
.name
,
306 dbus_error_free(&error
);
314 libhal_free_string_array(udis
);
318 static dbus_bool_t
device_has_capability(LibHalContext
*ctx
, const char *udi
,
319 const char* cap
, DBusError
*error
)
321 dbus_bool_t has_prop
;
322 has_prop
= libhal_device_property_exists(ctx
, udi
, "info.capabilities",
324 if (!has_prop
|| dbus_error_is_set(error
))
327 return libhal_device_query_capability(ctx
, udi
, cap
, error
);
330 static void device_added_time_cb(pa_mainloop_api
*ea
, pa_time_event
*ev
,
331 const struct timeval
*tv
, void *userdata
)
334 struct timerdata
*td
= (struct timerdata
*) userdata
;
336 dbus_error_init(&error
);
337 if (libhal_device_exists(td
->u
->ctx
, td
->udi
, &error
))
338 hal_device_add(td
->u
, td
->udi
, &error
);
340 if (dbus_error_is_set(&error
)) {
341 pa_log_error(__FILE__
": Error adding device: %s: %s", error
.name
,
343 dbus_error_free(&error
);
351 static void device_added_cb(LibHalContext
*ctx
, const char *udi
)
357 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
358 const char* cap
= get_capability_name(u
->capability
);
360 pa_log_debug(__FILE__
": HAL Device added: %s", udi
);
362 dbus_error_init(&error
);
363 has_cap
= device_has_capability(ctx
, udi
, cap
, &error
);
364 if (dbus_error_is_set(&error
)) {
365 pa_log_error(__FILE__
": Error getting capability: %s: %s", error
.name
,
367 dbus_error_free(&error
);
375 /* actually add the device 1/2 second later */
376 t
= pa_xnew(struct timerdata
, 1);
378 t
->udi
= pa_xstrdup(udi
);
380 pa_gettimeofday(&tv
);
381 pa_timeval_add(&tv
, 500000);
382 u
->core
->mainloop
->time_new(u
->core
->mainloop
, &tv
,
383 device_added_time_cb
, t
);
386 static void device_removed_cb(LibHalContext
* ctx
, const char *udi
)
389 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
391 pa_log_debug(__FILE__
": Device removed: %s", udi
);
392 if ((d
= pa_hashmap_remove(u
->devices
, udi
))) {
393 pa_module_unload_by_index(u
->core
, d
->index
);
398 static void new_capability_cb(LibHalContext
*ctx
, const char *udi
,
399 const char* capability
)
401 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
402 const char* capname
= get_capability_name(u
->capability
);
404 if (capname
&& !strcmp(capname
, capability
)) {
405 /* capability we care about, pretend it's a new device */
406 device_added_cb(ctx
, udi
);
410 static void lost_capability_cb(LibHalContext
*ctx
, const char *udi
,
411 const char* capability
)
413 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
414 const char* capname
= get_capability_name(u
->capability
);
416 if (capname
&& !strcmp(capname
, capability
)) {
417 /* capability we care about, pretend it was removed */
418 device_removed_cb(ctx
, udi
);
423 static void property_modified_cb(LibHalContext
*ctx
, const char *udi
,
425 dbus_bool_t is_removed
,
426 dbus_bool_t is_added
)
431 static void pa_hal_context_free(LibHalContext
* hal_ctx
)
435 dbus_error_init(&error
);
436 libhal_ctx_shutdown(hal_ctx
, &error
);
437 libhal_ctx_free(hal_ctx
);
439 if (dbus_error_is_set(&error
)) {
440 dbus_error_free(&error
);
444 static void userdata_free(struct userdata
*u
) {
445 pa_hal_context_free(u
->ctx
);
446 /* free the devices with the hashmap */
447 pa_hashmap_free(u
->devices
, hal_device_free_cb
, NULL
);
448 pa_dbus_connection_unref(u
->conn
);
452 static LibHalContext
* pa_hal_context_new(pa_core
* c
, DBusConnection
*conn
)
455 LibHalContext
*hal_ctx
= NULL
;
457 dbus_error_init(&error
);
458 if (!(hal_ctx
= libhal_ctx_new())) {
459 pa_log_error(__FILE__
": libhal_ctx_new() failed");
463 if (!libhal_ctx_set_dbus_connection(hal_ctx
, conn
)) {
464 pa_log_error(__FILE__
": Error establishing DBUS connection: %s: %s",
465 error
.name
, error
.message
);
469 if (!libhal_ctx_init(hal_ctx
, &error
)) {
470 pa_log_error(__FILE__
": Couldn't connect to hald: %s: %s",
471 error
.name
, error
.message
);
479 pa_hal_context_free(hal_ctx
);
481 if (dbus_error_is_set(&error
))
482 dbus_error_free(&error
);
487 int pa__init(pa_core
*c
, pa_module
*m
) {
490 pa_dbus_connection
*conn
;
491 struct userdata
*u
= NULL
;
492 LibHalContext
*hal_ctx
= NULL
;
497 dbus_error_init(&error
);
498 if (!(conn
= pa_dbus_bus_get(c
, DBUS_BUS_SYSTEM
, &error
))) {
499 pa_log_error(__FILE__
": Unable to contact DBUS system bus: %s: %s",
500 error
.name
, error
.message
);
501 dbus_error_free(&error
);
505 if (!(hal_ctx
= pa_hal_context_new(c
, pa_dbus_connection_get(conn
)))) {
506 /* pa_hal_context_new() logs appropriate errors */
510 u
= pa_xnew(struct userdata
, 1);
514 u
->devices
= pa_hashmap_new(pa_idxset_string_hash_func
,
515 pa_idxset_string_compare_func
);
516 m
->userdata
= (void*) u
;
519 if ((n
= hal_device_add_all(u
, CAP_ALSA
)) <= 0)
522 if ((n
= hal_device_add_all(u
, CAP_OSS
)) <= 0)
525 pa_log_warn(__FILE__
": failed to detect any sound hardware.");
530 libhal_ctx_set_user_data(hal_ctx
, (void*) u
);
531 libhal_ctx_set_device_added(hal_ctx
, device_added_cb
);
532 libhal_ctx_set_device_removed(hal_ctx
, device_removed_cb
);
533 libhal_ctx_set_device_new_capability(hal_ctx
, new_capability_cb
);
534 libhal_ctx_set_device_lost_capability(hal_ctx
, lost_capability_cb
);
535 /*libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);*/
537 dbus_error_init(&error
);
538 if (!libhal_device_property_watch_all(hal_ctx
, &error
)) {
539 pa_log_error(__FILE__
": error monitoring device list: %s: %s",
540 error
.name
, error
.message
);
541 dbus_error_free(&error
);
546 pa_log_info(__FILE__
": loaded %i modules.", n
);
552 void pa__done(PA_GCC_UNUSED pa_core
*c
, pa_module
*m
) {
555 /* free the user data */
556 userdata_free(m
->userdata
);