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>
38 #include <pulsecore/core-error.h>
39 #include <pulsecore/module.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/core-subscribe.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
)
55 static const char*const capabilities
[] = { "alsa", "oss" };
79 capability_t capability
;
80 pa_dbus_connection
*conn
;
82 pa_hashmap
*by_module
;
90 static alsa_type_t
hal_device_get_alsa_type(LibHalContext
*ctx
, const char *udi
,
96 type
= libhal_device_get_property_string(ctx
, udi
, "alsa.type", error
);
97 if (!type
|| dbus_error_is_set(error
))
100 if (!strcmp(type
, "playback")) {
102 } else if (!strcmp(type
, "capture")) {
103 t
= ALSA_TYPE_SOURCE
;
107 libhal_free_string(type
);
112 static int hal_device_get_alsa_card(LibHalContext
*ctx
, const char *udi
,
115 return libhal_device_get_property_int(ctx
, udi
, "alsa.card", error
);
118 static int hal_device_get_alsa_device(LibHalContext
*ctx
, const char *udi
,
121 return libhal_device_get_property_int(ctx
, udi
, "alsa.device", error
);
124 static void hal_device_free(struct device
* d
) {
129 static void hal_device_free_cb(void *d
, PA_GCC_UNUSED
void *data
) {
130 hal_device_free((struct device
*) d
);
133 static dbus_bool_t
hal_device_add_alsa(struct userdata
*u
, const char *udi
,
141 const char *module_name
;
143 type
= hal_device_get_alsa_type(u
->ctx
, udi
, error
);
144 if (dbus_error_is_set(error
) || type
== ALSA_TYPE_OTHER
) {
148 device
= hal_device_get_alsa_device(u
->ctx
, udi
, error
);
149 if (dbus_error_is_set(error
) || device
!= 0)
152 card
= hal_device_get_alsa_card(u
->ctx
, udi
, error
);
153 if (dbus_error_is_set(error
))
156 module_name
= (type
== ALSA_TYPE_SINK
) ? "module-alsa-sink"
157 : "module-alsa-source";
158 snprintf(args
, sizeof(args
), "device=hw:%u", card
);
159 if (!(m
= pa_module_load(u
->core
, module_name
, args
)))
162 d
= pa_xmalloc(sizeof(struct device
));
163 d
->udi
= pa_xstrdup(udi
);
166 pa_hashmap_put(u
->by_module
, m
, d
);
167 pa_hashmap_put(u
->by_udi
, udi
, d
);
172 static int hal_device_add_all(struct userdata
*u
, capability_t capability
)
178 const char* cap
= capabilities
[capability
];
180 assert(capability
< CAP_MAX
);
182 pa_log_info(__FILE__
": Trying capability %u (%s)", capability
, cap
);
183 dbus_error_init(&error
);
184 udis
= libhal_find_device_by_capability(u
->ctx
, cap
, &n
, &error
);
185 if (dbus_error_is_set(&error
)) {
186 pa_log_error(__FILE__
": Error finding devices: %s: %s", error
.name
,
188 dbus_error_free(&error
);
192 for (i
= 0; i
< n
; ++i
) {
195 r
= hal_device_add_alsa(u
, udis
[i
], &error
);
198 /* r = hal_device_add_oss(u, udis[i], &error)
206 if (dbus_error_is_set(&error
)) {
207 pa_log_error(__FILE__
": Error adding device: %s: %s", error
.name
,
209 dbus_error_free(&error
);
217 libhal_free_string_array(udis
);
218 u
->capability
= capability
;
222 static dbus_bool_t
device_has_capability(LibHalContext
*ctx
, const char *udi
,
223 const char* cap
, DBusError
*error
)
225 dbus_bool_t has_prop
;
226 has_prop
= libhal_device_property_exists(ctx
, udi
, "info.capabilities",
228 if (!has_prop
|| dbus_error_is_set(error
))
231 return libhal_device_query_capability(ctx
, udi
, cap
, error
);
234 static void device_added_time_cb(pa_mainloop_api
*ea
, pa_time_event
*ev
,
235 const struct timeval
*tv
, void *userdata
)
238 struct timerdata
*td
= (struct timerdata
*) userdata
;
240 dbus_error_init(&error
);
241 if (!libhal_device_exists(td
->u
->ctx
, td
->udi
, &error
))
244 switch(td
->u
->capability
) {
246 hal_device_add_alsa(td
->u
, td
->udi
, &error
);
249 /* hal_device_add_oss(td->u, td->udi, &error);
259 if (dbus_error_is_set(&error
)) {
260 pa_log_error(__FILE__
": Error adding device: %s: %s", error
.name
,
262 dbus_error_free(&error
);
270 static void device_added_cb(LibHalContext
*ctx
, const char *udi
)
276 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
277 const char* cap
= capabilities
[u
->capability
];
279 pa_log_debug(__FILE__
": HAL Device added: %s", udi
);
281 dbus_error_init(&error
);
282 has_cap
= device_has_capability(ctx
, udi
, cap
, &error
);
283 if (dbus_error_is_set(&error
)) {
284 pa_log_error(__FILE__
": Error getting capability: %s: %s", error
.name
,
286 dbus_error_free(&error
);
294 /* actually add the device one second later */
295 t
= pa_xmalloc(sizeof(struct timerdata
));
297 t
->udi
= pa_xstrdup(udi
);
299 gettimeofday(&tv
, NULL
);
301 u
->core
->mainloop
->time_new(u
->core
->mainloop
, &tv
,
302 device_added_time_cb
, t
);
305 static void device_removed_cb(LibHalContext
* ctx
, const char *udi
)
308 struct userdata
*u
= (struct userdata
*) libhal_ctx_get_user_data(ctx
);
310 pa_log_debug(__FILE__
": Device removed: %s", udi
);
311 if ((d
= pa_hashmap_remove(u
->by_udi
, udi
))) {
312 d
= pa_hashmap_remove(u
->by_module
, d
->module
);
313 pa_log_debug(__FILE__
": Unloading: %s <%s>", d
->module
->name
, d
->module
->argument
);
314 pa_module_unload_request(d
->module
);
320 static void new_capability_cb(LibHalContext
*ctx
, const char *udi
,
321 const char* capability
)
325 static void lost_capability_cb(LibHalContext
*ctx
, const char *udi
,
326 const char* capability
)
330 static void property_modified_cb(LibHalContext
*ctx
, const char *udi
,
332 dbus_bool_t is_removed
,
333 dbus_bool_t is_added
)
338 static void subscribe_notify_cb(pa_core
*c
, pa_subscription_event_type_t type
,
339 uint32_t idx
, void *userdata
)
343 struct userdata
*u
= (struct userdata
*) userdata
;
345 /* only listen for module remove events */
346 if (type
!= (PA_SUBSCRIPTION_EVENT_MODULE
|PA_SUBSCRIPTION_EVENT_REMOVE
))
349 if (!(m
= pa_idxset_get_by_index(c
->modules
, idx
)))
352 /* we found the module, see if it's one we care about */
353 if ((d
= pa_hashmap_remove(u
->by_module
, m
))) {
354 pa_log_debug(__FILE__
": Removing module #%u %s: %s",
355 m
->index
, m
->name
, d
->udi
);
356 d
= pa_hashmap_remove(u
->by_udi
, d
->udi
);
362 static void pa_hal_context_free(LibHalContext
* hal_ctx
)
366 dbus_error_init(&error
);
367 libhal_ctx_shutdown(hal_ctx
, &error
);
368 libhal_ctx_free(hal_ctx
);
370 if (dbus_error_is_set(&error
)) {
371 dbus_error_free(&error
);
375 static void userdata_free(struct userdata
*u
) {
376 pa_hal_context_free(u
->ctx
);
377 pa_subscription_free(u
->sub
);
378 /* free the hashmap */
379 pa_hashmap_free(u
->by_module
, NULL
, NULL
);
380 /* free the devices with the hashmap */
381 pa_hashmap_free(u
->by_udi
, hal_device_free_cb
, NULL
);
382 pa_dbus_connection_unref(u
->conn
);
386 static LibHalContext
* pa_hal_context_new(pa_core
* c
, DBusConnection
*conn
)
389 LibHalContext
*hal_ctx
= NULL
;
391 dbus_error_init(&error
);
392 if (!(hal_ctx
= libhal_ctx_new())) {
393 pa_log_error(__FILE__
": libhal_ctx_new() failed");
397 if (!libhal_ctx_set_dbus_connection(hal_ctx
, conn
)) {
398 pa_log_error(__FILE__
": Error establishing DBUS connection: %s: %s",
399 error
.name
, error
.message
);
403 if (!libhal_ctx_init(hal_ctx
, &error
)) {
404 pa_log_error(__FILE__
": Couldn't connect to hald: %s: %s",
405 error
.name
, error
.message
);
413 pa_hal_context_free(hal_ctx
);
415 if (dbus_error_is_set(&error
))
416 dbus_error_free(&error
);
421 int pa__init(pa_core
*c
, pa_module
*m
) {
424 pa_dbus_connection
*conn
;
425 struct userdata
*u
= NULL
;
426 LibHalContext
*hal_ctx
= NULL
;
431 dbus_error_init(&error
);
432 if (!(conn
= pa_dbus_bus_get(c
, DBUS_BUS_SYSTEM
, &error
))) {
433 pa_log_error(__FILE__
": Unable to contact DBUS system bus: %s: %s",
434 error
.name
, error
.message
);
435 dbus_error_free(&error
);
439 if (!(hal_ctx
= pa_hal_context_new(c
, pa_dbus_connection_get(conn
)))) {
440 /* pa_hal_context_new() logs appropriate errors */
444 u
= pa_xmalloc(sizeof(struct userdata
));
448 u
->by_module
= pa_hashmap_new(NULL
, NULL
);
449 u
->by_udi
= pa_hashmap_new(pa_idxset_string_hash_func
,
450 pa_idxset_string_compare_func
);
451 u
->sub
= pa_subscription_new(c
, PA_SUBSCRIPTION_MASK_MODULE
,
452 subscribe_notify_cb
, (void*) u
);
453 m
->userdata
= (void*) u
;
456 if ((n
= hal_device_add_all(u
, CAP_ALSA
)) <= 0)
459 if ((n
= hal_device_add_all(u
, CAP_OSS
)) <= 0)
462 pa_log_warn(__FILE__
": failed to detect any sound hardware.");
467 libhal_ctx_set_user_data(hal_ctx
, (void*) u
);
468 libhal_ctx_set_device_added(hal_ctx
, device_added_cb
);
469 libhal_ctx_set_device_removed(hal_ctx
, device_removed_cb
);
471 dbus_error_init(&error
);
472 if (!libhal_device_property_watch_all(hal_ctx
, &error
)) {
473 pa_log_error(__FILE__
": error monitoring device list: %s: %s",
474 error
.name
, error
.message
);
475 dbus_error_free(&error
);
481 libhal_ctx_set_device_new_capability(hal_ctx, new_capability_cb);
482 libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability_cb);
483 libhal_ctx_set_device_property_modified(hal_ctx, property_modified_cb);
486 pa_log_info(__FILE__
": loaded %i modules.", n
);
492 void pa__done(PA_GCC_UNUSED pa_core
*c
, pa_module
*m
) {
495 /* free the user data */
496 userdata_free(m
->userdata
);