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); | 
