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
;
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")) {
203 device
= libhal_device_get_property_string(ctx
, udi
, "oss.device_file",
205 if (!device
|| dbus_error_is_set(error
))
208 /* hack to ignore /dev/audio style devices */
209 if ((device
= strrchr(device
, '/')))
210 rv
= (pa_startswith(device
+ 1, "audio")) ? FALSE
: TRUE
;
214 libhal_free_string(type
);
218 static pa_module
* hal_device_load_oss(struct userdata
*u
, const char *udi
,
224 if (!hal_device_is_oss_pcm(u
->ctx
, udi
, error
) || dbus_error_is_set(error
))
227 device
= libhal_device_get_property_string(u
->ctx
, udi
, "oss.device_file",
229 if (!device
|| dbus_error_is_set(error
))
232 snprintf(args
, sizeof(args
), "device=%s sink_name=oss_output.%s source_name=oss_input.%s", device
, strip_udi(udi
), strip_udi(udi
));
233 libhal_free_string(device
);
235 return pa_module_load(u
->core
, "module-oss", args
);
239 static dbus_bool_t
hal_device_add(struct userdata
*u
, const char *udi
,
245 switch(u
->capability
) {
248 m
= hal_device_load_alsa(u
, udi
, error
);
253 m
= hal_device_load_oss(u
, udi
, error
);
257 assert(FALSE
); /* never reached */
261 if (!m
|| dbus_error_is_set(error
))
264 d
= pa_xnew(struct device
, 1);
265 d
->udi
= pa_xstrdup(udi
);
268 pa_hashmap_put(u
->devices
, d
->udi
, d
);
273 static int hal_device_add_all(struct userdata
*u
, capability_t capability
)
279 const char* cap
= get_capability_name(capability
);
281 assert(capability
< CAP_MAX
);
283 pa_log_info(__FILE__
": Trying capability %u (%s)", capability
, cap
);
284 dbus_error_init(&error
);
285 udis
= libhal_find_device_by_capability(u
->ctx
, cap
, &n
, &error
);
286 if (dbus_error_is_set(&error
)) {
287 pa_log_error(__FILE__
": Error finding devices: %s: %s", error
.name
,
289 dbus_error_free(&error
);
293 u
->capability
= capability
;
294 for (i
= 0; i
< n
; ++i
) {
295 r
= hal_device_add(u
, udis
[i
], &error
);
296 if (dbus_error_is_set(&error
)) {
297 pa_log_error(__FILE__
": Error adding device: %s: %s", error
.name
,
299 dbus_error_free(&error
);
307 libhal_free_string_array(udis
);
311 static dbus_bool_t
device_has_capability(LibHalContext
*ctx
, const char *udi
,
312 const char* cap
, DBusError
*error
)
314 dbus_bool_t has_prop
;
315 has_prop
= libhal_device_property_exists(ctx
, udi
, "info.capabilities",
317 if (!has_prop
|| dbus_error_is_set(error
))
320 return libhal_device_query_capability(ctx
, udi
, cap
, error
);
323 static void device_added_time_cb(pa_mainloop_api
*ea
, pa_time_event
*ev
,
324 const struct timeval
*tv
, void *userdata
)
327 struct timerdata
*td
= (struct timerdata
*) userdata
;
329 dbus_error_init(&error
);
330 if (libhal_device_exists(td
->u
->ctx
, td
->udi
, &error
))
331 hal_device_add(td
->u
, td
->udi
, &error
);
333 if (dbus_error_is_set(&error
)) {
334 pa_log_error(__FILE__
": Error adding device: %s: %s", error
.name
,
336 dbus_error_free(&error
);
344 static void device_added_cb(LibHalContext
*ctx
, const char *udi
)
350 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
351 const char* cap
= get_capability_name(u
->capability
);
353 pa_log_debug(__FILE__
": HAL Device added: %s", udi
);
355 dbus_error_init(&error
);
356 has_cap
= device_has_capability(ctx
, udi
, cap
, &error
);
357 if (dbus_error_is_set(&error
)) {
358 pa_log_error(__FILE__
": Error getting capability: %s: %s", error
.name
,
360 dbus_error_free(&error
);
368 /* actually add the device 1/2 second later */
369 t
= pa_xnew(struct timerdata
, 1);
371 t
->udi
= pa_xstrdup(udi
);
373 pa_gettimeofday(&tv
);
374 pa_timeval_add(&tv
, 500000);
375 u
->core
->mainloop
->time_new(u
->core
->mainloop
, &tv
,
376 device_added_time_cb
, t
);
379 static void device_removed_cb(LibHalContext
* ctx
, const char *udi
)
382 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
384 pa_log_debug(__FILE__
": Device removed: %s", udi
);
385 if ((d
= pa_hashmap_remove(u
->devices
, udi
))) {
386 pa_module_unload_by_index(u
->core
, d
->index
);
391 static void new_capability_cb(LibHalContext
*ctx
, const char *udi
,
392 const char* capability
)
394 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
395 const char* capname
= get_capability_name(u
->capability
);
397 if (capname
&& !strcmp(capname
, capability
)) {
398 /* capability we care about, pretend it's a new device */
399 device_added_cb(ctx
, udi
);
403 static void lost_capability_cb(LibHalContext
*ctx
, const char *udi
,
404 const char* capability
)
406 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
407 const char* capname
= get_capability_name(u
->capability
);
409 if (capname
&& !strcmp(capname
, capability
)) {
410 /* capability we care about, pretend it was removed */
411 device_removed_cb(ctx
, udi
);
416 static void property_modified_cb(LibHalContext
*ctx
, const char *udi
,
418 dbus_bool_t is_removed
,
419 dbus_bool_t is_added
)
424 static void pa_hal_context_free(LibHalContext
* hal_ctx
)
428 dbus_error_init(&error
);
429 libhal_ctx_shutdown(hal_ctx
, &error
);
430 libhal_ctx_free(hal_ctx
);
432 if (dbus_error_is_set(&error
)) {
433 dbus_error_free(&error
);
437 static void userdata_free(struct userdata
*u
) {
438 pa_hal_context_free(u
->ctx
);
439 /* free the devices with the hashmap */
440 pa_hashmap_free(u
->devices
, hal_device_free_cb
, NULL
);
441 pa_dbus_connection_unref(u
->conn
);
445 static LibHalContext
* pa_hal_context_new(pa_core
* c
, DBusConnection
*conn
)
448 LibHalContext
*hal_ctx
= NULL
;
450 dbus_error_init(&error
);
451 if (!(hal_ctx
= libhal_ctx_new())) {
452 pa_log_error(__FILE__
": libhal_ctx_new() failed");
456 if (!libhal_ctx_set_dbus_connection(hal_ctx
, conn
)) {
457 pa_log_error(__FILE__
": Error establishing DBUS connection: %s: %s",
458 error
.name
, error
.message
);
462 if (!libhal_ctx_init(hal_ctx
, &error
)) {
463 pa_log_error(__FILE__
": Couldn't connect to hald: %s: %s",
464 error
.name
, error
.message
);
472 pa_hal_context_free(hal_ctx
);
474 if (dbus_error_is_set(&error
))
475 dbus_error_free(&error
);
480 int pa__init(pa_core
*c
, pa_module
*m
) {
483 pa_dbus_connection
*conn
;
484 struct userdata
*u
= NULL
;
485 LibHalContext
*hal_ctx
= NULL
;
490 dbus_error_init(&error
);
491 if (!(conn
= pa_dbus_bus_get(c
, DBUS_BUS_SYSTEM
, &error
))) {
492 pa_log_error(__FILE__
": Unable to contact DBUS system bus: %s: %s",
493 error
.name
, error
.message
);
494 dbus_error_free(&error
);
498 if (!(hal_ctx
= pa_hal_context_new(c
, pa_dbus_connection_get(conn
)))) {
499 /* pa_hal_context_new() logs appropriate errors */
503 u
= pa_xnew(struct userdata
, 1);
507 u
->devices
= pa_hashmap_new(pa_idxset_string_hash_func
,
508 pa_idxset_string_compare_func
);
509 m
->userdata
= (void*) u
;
512 if ((n
= hal_device_add_all(u
, CAP_ALSA
)) <= 0)
515 if ((n
= hal_device_add_all(u
, CAP_OSS
)) <= 0)
518 pa_log_warn(__FILE__
": failed to detect any sound hardware.");
523 libhal_ctx_set_user_data(hal_ctx
, (void*) u
);
524 libhal_ctx_set_device_added(hal_ctx
, device_added_cb
);
525 libhal_ctx_set_device_removed(hal_ctx
, device_removed_cb
);
526 libhal_ctx_set_device_new_capability(hal_ctx
, new_capability_cb
);
527 libhal_ctx_set_device_lost_capability(hal_ctx
, lost_capability_cb
);
528 /*libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);*/
530 dbus_error_init(&error
);
531 if (!libhal_device_property_watch_all(hal_ctx
, &error
)) {
532 pa_log_error(__FILE__
": error monitoring device list: %s: %s",
533 error
.name
, error
.message
);
534 dbus_error_free(&error
);
539 pa_log_info(__FILE__
": loaded %i modules.", n
);
545 void pa__done(PA_GCC_UNUSED pa_core
*c
, pa_module
*m
) {
548 /* free the user data */
549 userdata_free(m
->userdata
);