diff options
-rw-r--r-- | audio/audio-api.txt | 36 | ||||
-rw-r--r-- | audio/headset.c | 288 | ||||
-rw-r--r-- | audio/headset.h | 6 | ||||
-rw-r--r-- | audio/main.c | 2 | ||||
-rw-r--r-- | audio/manager.c | 282 | ||||
-rw-r--r-- | audio/manager.h | 32 |
6 files changed, 396 insertions, 250 deletions
diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 26b4d46a..1f71c14e 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -2,14 +2,13 @@ Bluetooth audio service API description *************************************** Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> -Copyright (C) 2005-2006 Johan Hedberg <johan.hedberg@nokia.com> +Copyright (C) 2005-2007 Johan Hedberg <johan.hedberg@nokia.com> Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com> -Audio Manager hierarchy -======================= +org.bluez.audio.Manager interface +================================= -Interface org.bluez.audio.Manager Object path /org/bluez/audio Methods array{string} ListHeadsets() @@ -49,11 +48,20 @@ Signals void HeadsetCreated(string path) Sent when the default headset has changed. -Audio Headset hierarchy -======================= +org.bluez.audio.Device interface +================================ -Interface org.bluez.audio.Headset -Object path /org/bluez/audio/headset* +Object path(s) /org/bluez/audio/device* + +Methods string GetAddress() + + Returns the Bluetooth address of the remote device. + + +org.bluez.audio.Headset interface +================================= + +Object path(s) /org/bluez/audio/device* Methods void Connect() @@ -111,3 +119,15 @@ Signals void AnswerRequested() void MicrophoneGainChanged(uint16 gain) The microphone gain changed. + + +org.bluez.audio.Source interface +================================ + +Object path(s) /org/bluez/audio/device* + + +org.bluez.audio.Sink interface +============================== + +Object path(s) /org/bluez/audio/device* diff --git a/audio/headset.c b/audio/headset.c index 5d991fb4..72fc3bfb 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -67,8 +67,7 @@ typedef enum { } headset_event_t; typedef enum { - HEADSET_STATE_UNAUTHORIZED, - HEADSET_STATE_DISCONNECTED, + HEADSET_STATE_DISCONNECTED = 0, HEADSET_STATE_CONNECT_IN_PROGRESS, HEADSET_STATE_CONNECTED, HEADSET_STATE_PLAY_IN_PROGRESS, @@ -82,9 +81,6 @@ struct pending_connect { }; struct headset { - char object_path[128]; - bdaddr_t bda; - GIOChannel *rfcomm; GIOChannel *sco; @@ -110,8 +106,6 @@ static GIOChannel *hs_server = NULL; static uint32_t hs_record_id = 0; -static GSList *headsets = NULL; - static DBusConnection *connection = NULL; static void pending_connect_free(struct pending_connect *c) @@ -169,13 +163,6 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) return error_reply(conn, msg, "org.bluez.audio.Error.Failed", "Failed"); } -static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) -{ - const struct headset *hs = headset; - - return bacmp(&hs->bda, bda); -} - static gboolean headset_close_output(struct headset *hs) { assert(hs != NULL); @@ -269,7 +256,7 @@ static gboolean headset_open_input(struct headset *hs, const char *input) } #endif -static void hs_signal_gain_setting(struct headset *hs, const char *buf) +static void hs_signal_gain_setting(audio_device_t *device, const char *buf) { const char *name; dbus_uint16_t gain; @@ -293,7 +280,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf) gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); @@ -321,8 +308,10 @@ static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_l return HEADSET_EVENT_UNKNOWN; } -static void close_sco(struct headset *hs) +static void close_sco(audio_device_t *device) { + struct headset *hs = device->headset; + g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; @@ -334,15 +323,15 @@ static void close_sco(struct headset *hs) headset_close_input(hs); assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Stopped", DBUS_TYPE_INVALID); } - static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, - struct headset *hs) + audio_device_t *device) { + struct headset *hs = device->headset; unsigned char buf[BUF_SIZE]; char *cr, rsp[BUF_SIZE]; gsize bytes_read = 0; @@ -387,7 +376,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, switch (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp))) { case HEADSET_EVENT_GAIN: - hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); + hs_signal_gain_setting(device, &hs->buf[hs->data_start] + 2); break; case HEADSET_EVENT_KEYPRESS: @@ -396,7 +385,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, hs->ring_timer = 0; } - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); @@ -432,17 +421,17 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, failed: if (hs->sco) - close_sco(hs); - hs_disconnect(NULL, NULL, hs); + close_sco(device); + hs_disconnect(NULL, NULL, device); return FALSE; } -static void send_cancel_auth(struct headset *hs) +static void send_cancel_auth(audio_device_t *device) { DBusMessage *cancel; char addr[18], *address = addr; - const char *uuid = ""; + const char *uuid = HSP_AG_UUID; cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", @@ -452,7 +441,7 @@ static void send_cancel_auth(struct headset *hs) return; } - ba2str(&hs->bda, addr); + ba2str(&device->bda, addr); dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); @@ -462,7 +451,8 @@ static void send_cancel_auth(struct headset *hs) static void auth_callback(DBusPendingCall *call, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; @@ -471,7 +461,7 @@ static void auth_callback(DBusPendingCall *call, void *data) error("Access denied: %s", err.message); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); - send_cancel_auth(hs); + send_cancel_auth(device); } dbus_error_free(&err); g_io_channel_close(hs->rfcomm); @@ -481,14 +471,15 @@ static void auth_callback(DBusPendingCall *call, void *data) char hs_address[18]; g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, hs); + (GIOFunc) rfcomm_io_cb, device); - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); - debug("Accepted connection from %s for %s", hs_address, hs->object_path); + debug("Accepted headset connection from %s for %s", hs_address, + device->object_path); hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); @@ -499,7 +490,8 @@ static void auth_callback(DBusPendingCall *call, void *data) static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; char buf[1024]; gsize bytes_read; gsize bytes_written, total_bytes_written; @@ -536,7 +528,8 @@ failed: static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; char buf[1024]; gsize bytes_read; gsize bytes_written, total_bytes_written; @@ -579,13 +572,14 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond disconn: error("Audio connection got disconnected"); if (hs->sco) - close_sco(hs); + close_sco(device); return FALSE; } static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, - struct headset *hs) + audio_device_t *device) { + struct headset *hs = device->headset; int ret, sk, err, flags; socklen_t len; DBusMessage *reply; @@ -611,7 +605,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } - debug("SCO socket opened for headset %s", hs->object_path); + debug("SCO socket opened for headset %s", device->object_path); hs->sco = chan; hs->pending_connect->io = NULL; @@ -629,13 +623,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ if (hs->audio_input) g_io_add_watch(hs->audio_input, G_IO_IN | G_IO_NVAL, - audio_input_to_sco_cb, hs); + audio_input_to_sco_cb, device); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_PLAYING; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Playing", DBUS_TYPE_INVALID); @@ -654,8 +648,10 @@ failed: return FALSE; } -static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs) +static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, + audio_device_t *device) { + struct headset *hs = device->headset; char hs_address[18]; int sk, ret, err; socklen_t len; @@ -682,19 +678,19 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he goto failed; } - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); hs->rfcomm = chan; hs->pending_connect->io = NULL; hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); debug("Connected to %s", hs_address); g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, hs); + (GIOFunc) rfcomm_io_cb, device); if (hs->pending_connect->msg) { DBusMessage *reply; @@ -721,8 +717,9 @@ failed: return FALSE; } -static int rfcomm_connect(struct headset *hs, int *err) +static int rfcomm_connect(audio_device_t *device, int *err) { + struct headset *hs = device->headset; struct sockaddr_rc addr; char address[18]; int sk; @@ -730,7 +727,7 @@ static int rfcomm_connect(struct headset *hs, int *err) assert(hs != NULL && hs->pending_connect != NULL && hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - ba2str(&hs->bda, address); + ba2str(&device->bda, address); debug("Connecting to %s channel %d", address, hs->pending_connect->ch); @@ -761,7 +758,7 @@ static int rfcomm_connect(struct headset *hs, int *err) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &hs->bda); + bacpy(&addr.rc_bdaddr, &device->bda); addr.rc_channel = hs->pending_connect->ch; hs->pending_connect->io = g_io_channel_unix_new(sk); @@ -781,10 +778,10 @@ static int rfcomm_connect(struct headset *hs, int *err) debug("Connect in progress"); g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL, - (GIOFunc) rfcomm_connect_cb, hs); + (GIOFunc) rfcomm_connect_cb, device); } else { debug("Connect succeeded with first try"); - rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, hs); + rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device); } return 0; @@ -986,7 +983,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; struct pending_connect *c; assert(hs != NULL && hs->pending_connect && !hs->rfcomm); @@ -1053,7 +1051,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - if (rfcomm_connect(hs, &err) < 0) { + if (rfcomm_connect(device, &err) < 0) { error("Unable to connect"); if (c->msg) err_connect_failed(connection, c->msg, err); @@ -1064,7 +1062,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); - finish_sdp_transaction(&hs->bda); + finish_sdp_transaction(&device->bda); return; @@ -1081,13 +1079,14 @@ failed: pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; - finish_sdp_transaction(&hs->bda); + finish_sdp_transaction(&device->bda); } static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; if (!hs || !hs->sco) @@ -1109,7 +1108,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, hs->state = HEADSET_STATE_CONNECTED; } - close_sco(hs); + close_sco(device); if (reply) send_message_and_unref(connection, reply); @@ -1120,7 +1119,8 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t playing; @@ -1146,7 +1146,8 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; char hs_address[18]; @@ -1179,10 +1180,10 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, hs->state = HEADSET_STATE_DISCONNECTED; - ba2str(&hs->bda, hs_address); - info("Disconnected from %s, %s", &hs_address, hs->object_path); + ba2str(&device->bda, hs_address); + info("Disconnected from %s, %s", hs_address, device->object_path); - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); @@ -1198,7 +1199,8 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t connected; @@ -1226,7 +1228,8 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; struct pending_connect *c; char address[18], *addr_ptr = address; dbus_uint32_t *array = NULL; @@ -1288,7 +1291,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) goto failed; } - ba2str(&hs->bda, address); + ba2str(&device->bda, address); handle = array[0]; @@ -1303,7 +1306,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) goto failed; } - dbus_pending_call_set_notify(pending, get_record_reply, hs, NULL); + dbus_pending_call_set_notify(pending, get_record_reply, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(msg); dbus_message_unref(reply); @@ -1321,7 +1324,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, void *data) { DBusPendingCall *pending; - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; const char *hs_svc = "hsp"; const char *addr_ptr; char hs_address[18]; @@ -1352,7 +1356,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; } - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); addr_ptr = hs_address; dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_STRING, &hs_svc, @@ -1367,22 +1371,23 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return err_connect_failed(connection, msg, EIO); } - dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); + dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(msg); return DBUS_HANDLER_RESULT_HANDLED;; } -static GIOError headset_send_ring(struct headset *hs) +static GIOError headset_send_ring(audio_device_t *device) { + struct headset *hs = device->headset; const char *ring_str = "\r\nRING\r\n"; GIOError err; gsize total_written, written, count; assert(hs != NULL); if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { - error("the headset %s is not connected", hs->object_path); + error("the headset %s is not connected", device->object_path); return G_IO_ERROR_UNKNOWN; } @@ -1402,11 +1407,11 @@ static GIOError headset_send_ring(struct headset *hs) static gboolean ring_timer_cb(gpointer data) { - struct headset *hs = data; + audio_device_t *device = data; - assert(hs != NULL); + assert(device != NULL); - if (headset_send_ring(hs) != G_IO_ERROR_NONE) + if (headset_send_ring(device) != G_IO_ERROR_NONE) error("Sending RING failed"); return TRUE; @@ -1415,7 +1420,8 @@ static gboolean ring_timer_cb(gpointer data) static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; assert(hs != NULL); @@ -1434,12 +1440,12 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, goto done; } - if (headset_send_ring(hs) != G_IO_ERROR_NONE) { + if (headset_send_ring(device) != G_IO_ERROR_NONE) { dbus_message_unref(reply); return err_failed(connection, msg); } - hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs); + hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); done: if (reply) @@ -1451,7 +1457,8 @@ done: static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) @@ -1481,7 +1488,8 @@ done: static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; struct sockaddr_sco addr; struct pending_connect *c; int sk, err; @@ -1537,7 +1545,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &hs->bda); + bacpy(&addr.sco_bdaddr, &device->bda); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -1549,10 +1557,10 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, debug("Connect in progress"); g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, - (GIOFunc) sco_connect_cb, hs); + (GIOFunc) sco_connect_cb, device); } else { debug("Connect succeeded with first try"); - sco_connect_cb(c->io, G_IO_OUT, hs); + sco_connect_cb(c->io, G_IO_OUT, device); } hs->pending_connect = c; @@ -1589,93 +1597,15 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -static struct headset *headset_add_internal(const bdaddr_t *bda) -{ - static int headset_uid = 0; - struct headset *hs; - GSList *match; - - match = g_slist_find_custom(headsets, bda, headset_bda_cmp); - if (match) - return match->data; - - hs = g_try_new0(struct headset, 1); - if (!hs) { - error("Allocating new hs connection struct failed!"); - return NULL; - } - - snprintf(hs->object_path, sizeof(hs->object_path), - HEADSET_PATH_BASE "%d", headset_uid++); - - if (!dbus_connection_create_object_path(connection, hs->object_path, - hs, NULL)) { - error("D-Bus failed to register %s path", hs->object_path); - g_free(hs); - return NULL; - } - - if (!dbus_connection_register_interface(connection, hs->object_path, - AUDIO_HEADSET_INTERFACE, - headset_methods, - headset_signals, NULL)) { - error("Failed to register %s interface to %s", - AUDIO_HEADSET_INTERFACE, hs->object_path); - dbus_connection_destroy_object_path(connection, - hs->object_path); - g_free(hs); - return NULL; - } - - bacpy(&hs->bda, bda); - - headsets = g_slist_append(headsets, hs); - - return hs; -} - -const char *headset_add(const bdaddr_t *bda) -{ - struct headset *hs; - - hs = headset_add_internal(bda); - if (!hs) - return NULL; - - return hs->object_path; -} - -const char *headset_get(const bdaddr_t *bda) +headset_t *headset_init(const char *object_path) { - GSList *match; - struct headset *hs; - - match = g_slist_find_custom(headsets, bda, headset_bda_cmp); - if (!match) + if (!dbus_connection_register_interface(connection, object_path, + AUDIO_HEADSET_INTERFACE, + headset_methods, + headset_signals, NULL)) return NULL; - hs = match->data; - - return hs->object_path; -} - -void headset_remove(const char *path) -{ - struct headset *hs; - - if (!dbus_connection_get_object_user_data(connection, path, - (void *) &hs)) - return; - - if (hs->state > HEADSET_STATE_DISCONNECTED) - hs_disconnect(NULL, NULL, hs); - - if (!dbus_connection_destroy_object_path(connection, path)) - error("D-Bus failed to unregister %s path", path); - - headsets = g_slist_remove(headsets, hs); - - g_free(hs); + return g_new0(headset_t, 1); } static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) @@ -1684,11 +1614,11 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * struct sockaddr_rc addr; socklen_t size; char hs_address[18], *address = hs_address; - const char *uuid = ""; - struct headset *hs = NULL; + const char *uuid = HSP_AG_UUID; + audio_device_t *device; + struct headset *hs; DBusMessage *auth; DBusPendingCall *pending; - GSList *match; if (cond & G_IO_NVAL) return FALSE; @@ -1709,19 +1639,13 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * return TRUE; } - match = g_slist_find_custom(headsets, &addr.rc_bdaddr, headset_bda_cmp); - if (!match) { - hs = headset_add_internal(&addr.rc_bdaddr); - if (!hs) { - error("Unable to create a new headset object"); - close(cli_sk); - return TRUE; - } - - manager_add_headset(hs->object_path); + device = manager_headset_connected(&addr.rc_bdaddr); + if (!device) { + close(cli_sk); + return TRUE; } - else - hs = match->data; + + hs = device->headset; if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); @@ -1743,7 +1667,7 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * goto failed; } - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); @@ -1753,7 +1677,7 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * goto failed; } - dbus_pending_call_set_notify(pending, auth_callback, hs, NULL); + dbus_pending_call_set_notify(pending, auth_callback, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(auth); @@ -1820,7 +1744,7 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } -int headset_init(DBusConnection *conn) +int headset_server_init(DBusConnection *conn) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; diff --git a/audio/headset.h b/audio/headset.h index ae5119fc..de8e8a38 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -31,13 +31,15 @@ #define BUF_SIZE 1024 +typedef struct headset headset_t; + const char *headset_get(const bdaddr_t *bda); const char *headset_add(const bdaddr_t *bda); -void headset_remove(const char *path); +headset_t *headset_init(const char *path); -int headset_init(DBusConnection *conn); +int headset_server_init(DBusConnection *conn); void headset_exit(void); diff --git a/audio/main.c b/audio/main.c index 2188e6e7..5c44df9b 100644 --- a/audio/main.c +++ b/audio/main.c @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_init(conn) < 0) { + if (headset_server_init(conn) < 0) { error("Headset initialization failed!"); exit(1); } diff --git a/audio/manager.c b/audio/manager.c index 5d381972..682158c2 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -56,9 +56,9 @@ static DBusConnection *connection = NULL; -static char *default_hs = NULL; +static audio_device_t *default_hs = NULL; -static GSList *headsets = NULL; +static GSList *devices = NULL; static int unix_sock = -1; @@ -116,85 +116,242 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } -void manager_add_headset(const char *path) +static audio_device_t *find_device(bdaddr_t *bda) { - char *my_path = g_strdup(path); + GSList *l; + + for (l = devices; l != NULL; l = l->next) { + audio_device_t *device = l->data; + if (bacmp(&device->bda, bda) == 0) + return device; + } + + return NULL; +} + +static DBusHandlerResult device_get_address(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + audio_device_t *device = data; + DBusMessage *reply; + char address[18], *ptr = address; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - headsets = g_slist_append(headsets, my_path); + ba2str(&device->bda, address); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); +} + +static DBusMethodVTable device_methods[] = { + { "GetAddress", device_get_address, "", "s" }, + { NULL, NULL, NULL, NULL } +}; + +static audio_device_t *add_device(bdaddr_t *bda) +{ + static int device_id = 0; + audio_device_t *device; + + device = g_new0(audio_device_t, 1); + + bacpy(&device->bda, bda); + + snprintf(device->object_path, sizeof(device->object_path) - 1, + "%s/device%d", AUDIO_MANAGER_PATH, device_id++); + + if (!dbus_connection_create_object_path(connection, device->object_path, + device, NULL)) { + error("D-Bus failed to register %s path", device->object_path); + g_free(device); + return NULL; + } + + if (!dbus_connection_register_interface(connection, device->object_path, + AUDIO_DEVICE_INTERFACE, + device_methods, NULL, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_DEVICE_INTERFACE, device->object_path); + dbus_connection_destroy_object_path(connection, + device->object_path); + g_free(device); + return NULL; + } + + devices = g_slist_append(devices, device); + + return device; +} + +static void remove_device(audio_device_t *device) +{ + devices = g_slist_remove(devices, device); + dbus_connection_destroy_object_path(connection, device->object_path); + g_free(device->headset); + g_free(device); +} + +audio_device_t *manager_headset_connected(bdaddr_t *bda) +{ + audio_device_t *device; + const char *path; + + device = find_device(bda); + if (device && device->headset) + return device; + + if (!device) + device = add_device(bda); + + if (!device) + return NULL; + + if (!device->headset) + device->headset = headset_init(device->object_path); + + if (!device->headset) + return NULL; + + path = device->object_path; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetCreated", - DBUS_TYPE_STRING, &my_path, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); if (!default_hs) { - default_hs = my_path; + default_hs = device; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", - DBUS_TYPE_STRING, &my_path, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); } -} -static void manager_remove_headset(char *path) -{ - headset_remove(path); - g_free(path); + return device; } -static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg, void *data) { - const char *hs_path; const char *address; bdaddr_t bda; - DBusMessage *reply; + audio_device_t *device; DBusError derr; + DBusMessageIter iter, array_iter; dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); + dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } + + str2ba(address, &bda); + + device = find_device(&bda); + if (device) { + const char *iface, *path = device->object_path; + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + "s", &array_iter); + if (device->headset) { + iface = AUDIO_HEADSET_INTERFACE; + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &iface); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(conn, reply); + } + + device = add_device(&bda); + /* + resolve_services(conn, device, msg); + */ + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *path, *address; + bdaddr_t bda; + DBusMessage *reply; + DBusError derr; + audio_device_t *device; + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } + str2ba(address, &bda); + + device = find_device(&bda); + if (!device) + device = add_device(&bda); + + if (!device) + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to create new audio device"); + + device->headset = headset_init(device->object_path); + if (!device->headset) + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to init Headset interface"); + + path = device->object_path; + reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - str2ba(address, &bda); - - hs_path = headset_get(&bda); - if (!hs_path) { - hs_path = headset_add(&bda); - if (!hs_path) - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to create new headset object"); - manager_add_headset(hs_path); - } - dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs_path, + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); } +static gint device_path_cmp(gconstpointer a, gconstpointer b) +{ + const audio_device_t *device = a; + const char *path = b; + + return strcmp(device->object_path, path); +} + static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *msg, void *data) { DBusError derr; DBusMessage *reply; GSList *match; - char *path; + const char *path; + audio_device_t *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -209,7 +366,7 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); + match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", @@ -219,19 +376,24 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - path = match->data; + device = match->data; - headsets = g_slist_remove(headsets, path); + remove_device(device); - if (default_hs == path) { + if (default_hs == device) { const char *param; + GSList *l; + + default_hs = NULL; - if (!headsets) - default_hs = NULL; - else - default_hs = headsets->data; + for (l = devices; l != NULL; l = l->next) { + device = l->data; - param = default_hs ? default_hs : ""; + if (device->headset) + default_hs = device; + } + + param = default_hs ? default_hs->object_path : ""; dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, @@ -246,10 +408,6 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - headset_remove(path); - - g_free(path); - return send_message_and_unref(connection, reply); } @@ -270,9 +428,18 @@ static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter); - for (l = headsets; l != NULL; l = l->next) + for (l = devices; l != NULL; l = l->next) { + audio_device_t *device = l->data; + const char *path; + + if (!device->headset) + continue; + + path = device->object_path; + dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &l->data); + DBUS_TYPE_STRING, &path); + } dbus_message_iter_close_container(&iter, &array_iter); @@ -283,6 +450,7 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessag void *data) { DBusMessage *reply; + const char *path; if (!default_hs) return error_reply(connection, msg, @@ -293,7 +461,9 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessag if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_hs, + path = default_hs->object_path; + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); @@ -320,7 +490,7 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); + match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", @@ -332,16 +502,20 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes default_hs = match->data; + path = default_hs->object_path; + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", - DBUS_TYPE_STRING, &default_hs, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); } static DBusMethodVTable manager_methods[] = { + { "CreateDevice", am_create_device, + "s", "sas" }, { "CreateHeadset", am_create_headset, "s", "s" }, { "RemoveHeadset", am_remove_headset, @@ -425,11 +599,9 @@ void audio_exit(void) unix_sock = -1; - if (headsets) { - g_slist_foreach(headsets, (GFunc) manager_remove_headset, NULL); - g_slist_free(headsets); - headsets = NULL; - } + g_slist_foreach(devices, (GFunc) remove_device, NULL); + g_slist_free(devices); + devices = NULL; dbus_connection_unref(connection); diff --git a/audio/manager.h b/audio/manager.h index 6356ab1c..9fd98d65 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -30,9 +30,37 @@ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" -#define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" +#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" -void manager_add_headset(const char *path); +#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" + +#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" +#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" + +#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" +#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" + +#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" + +#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" +#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" + +#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" +#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" + +typedef struct audio_device { + char object_path[128]; + bdaddr_t bda; + + headset_t *headset; +/* + sink_t *sink; + source_t *source; + control_t *control; +*/ +} audio_device_t; + +audio_device_t *manager_headset_connected(bdaddr_t *bda); int audio_init(DBusConnection *conn); |