diff options
| author | Johan Hedberg <johan.hedberg@nokia.com> | 2007-04-10 11:44:10 +0000 | 
|---|---|---|
| committer | Johan Hedberg <johan.hedberg@nokia.com> | 2007-04-10 11:44:10 +0000 | 
| commit | 35f12dee95378056a2ed7c0d325b2c3b68022da8 (patch) | |
| tree | 240c99b1f3d926fd19b00ff91513d264bf678e9f /audio/manager.c | |
| parent | 5daebe3831a85cc39a74c25798fb5123f2bdc633 (diff) | |
Audio service refactoring: move all manager functions to manager.c
Diffstat (limited to 'audio/manager.c')
| -rw-r--r-- | audio/manager.c | 323 | 
1 files changed, 319 insertions, 4 deletions
| diff --git a/audio/manager.c b/audio/manager.c index a7e3afd9..743e50d5 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -28,9 +28,14 @@  #include <stdio.h>  #include <errno.h>  #include <unistd.h> +#include <stdint.h> +#include <assert.h>  #include <sys/socket.h>  #include <sys/un.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +  #include <glib.h>  #include <dbus/dbus.h> @@ -38,6 +43,7 @@  #include "dbus.h"  #include "logging.h" +#include "headset.h"  #include "manager.h"  #ifndef UNIX_PATH_MAX @@ -46,10 +52,43 @@  #define SOCKET_NAME "/org/bluez/audio" -static DBusConnection *connection = NULL; +struct manager { +	DBusConnection *conn; +	GIOChannel *hs_server; +	uint32_t hs_record_id; +	int default_hs; +	GSList *headset_list; +}; + +static struct manager *manager = NULL;  static int unix_sock = -1; +/* FIXME: Remove these once global error functions exist */ +static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, +					const char *name, const char *descr) +{ +	DBusMessage *derr; + +	if (!conn || !msg) +		return DBUS_HANDLER_RESULT_HANDLED; + +	derr = dbus_message_new_error(msg, name, descr); +	if (!derr) { +	       	error("Unable to allocate new error return"); +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	} + +	return send_message_and_unref(conn, derr); +} + +static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, +						const char *descr) +{ +	return error_reply(conn, msg, "org.bluez.Error.InvalidArguments", +			descr ? descr : "Invalid arguments in method call"); +} +  static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)  {  	struct sockaddr_un addr; @@ -79,6 +118,276 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)  	return TRUE;  } +static GIOChannel *server_socket(uint8_t *channel) +{ +	int sock, lm; +	struct sockaddr_rc addr; +	socklen_t sa_len; +	GIOChannel *io; + +	sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +	if (sock < 0) { +		error("server socket: %s (%d)", strerror(errno), errno); +		return NULL; +	} + +	lm = RFCOMM_LM_SECURE; +	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { +		error("server setsockopt: %s (%d)", strerror(errno), errno); +		close(sock); +		return NULL; +	} + +	memset(&addr, 0, sizeof(addr)); +	addr.rc_family = AF_BLUETOOTH; +	bacpy(&addr.rc_bdaddr, BDADDR_ANY); +	addr.rc_channel = 0; + +	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		error("server bind: %s", strerror(errno), errno); +		close(sock); +		return NULL; +	} + +	if (listen(sock, 1) < 0) { +		error("server listen: %s", strerror(errno), errno); +		close(sock); +		return NULL; +	} + +	sa_len = sizeof(struct sockaddr_rc); +	getsockname(sock, (struct sockaddr *) &addr, &sa_len); +	*channel = addr.rc_channel; + +	io = g_io_channel_unix_new(sock); +	if (!io) { +		error("Unable to allocate new io channel"); +		close(sock); +		return NULL; +	} + +	return io; +} + +static gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan) +{ +	assert(manager != NULL); + +	if (manager->hs_server) { +		error("Server socket already created"); +		return FALSE; +	} + +	manager->hs_server = server_socket(&chan); +	if (!manager->hs_server) +		return FALSE; + +	if (!manager->hs_record_id) +		manager->hs_record_id = headset_add_ag_record(manager->conn, chan); + +	if (!manager->hs_record_id) { +		error("Unable to register service record"); +		g_io_channel_unref(manager->hs_server); +		manager->hs_server = NULL; +		return FALSE; +	} + +	g_io_add_watch(manager->hs_server, +			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, +			(GIOFunc) headset_server_io_cb, manager); + +	return TRUE; +} + +struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *bda) +{ +	GSList *elem; + +	assert(manager); +	elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp); + +	return elem ? elem->data : NULL; +} + +void manager_add_headset(struct manager *manager, struct headset *hs) +{ +	assert(manager && hs); + +	if (g_slist_find(manager->headset_list, hs)) +		return; + +	manager->headset_list = g_slist_append(manager->headset_list, hs); +} + +static DBusHandlerResult am_create_headset(struct manager *manager,  +						DBusMessage *msg) +{ +	const char *object_path; +	const char *address; +	struct headset *hs; +	bdaddr_t bda; +	DBusMessage *reply; +	DBusError derr; + +	dbus_error_init(&derr); +	if (!dbus_message_get_args(msg, &derr, +					DBUS_TYPE_STRING, &address, +					DBUS_TYPE_INVALID)) { +		err_invalid_args(manager->conn, msg, derr.message); +		return DBUS_HANDLER_RESULT_HANDLED; +	} +	if (dbus_error_is_set(&derr)) { +		err_invalid_args(manager->conn, msg, derr.message); +		dbus_error_free(&derr); +		return DBUS_HANDLER_RESULT_HANDLED; +	} + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	str2ba(address, &bda); +	hs = manager_find_headset_by_bda(manager, &bda); +	if (!hs) { +		hs = audio_headset_new(manager->conn, &bda); +		if (!hs) +			return error_reply(manager->conn, msg, +					"org.bluez.Error.Failed", +					"Unable to create new headset object"); +		manager_add_headset(manager, hs); +	} + +	object_path = audio_headset_get_path(hs); +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, +					DBUS_TYPE_INVALID); + +	return send_message_and_unref(manager->conn, reply); +} + +static DBusHandlerResult am_remove_headset(struct manager *manager,  +						DBusMessage *msg) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_list_headsets(struct manager *manager,  +						DBusMessage *msg) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_get_default_headset(struct manager *manager,  +						DBusMessage *msg) +{ +	DBusMessage *reply; +	char object_path[128]; +	const char *opath = object_path; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", +			manager->default_hs); +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, +					DBUS_TYPE_INVALID); + +	return send_message_and_unref(manager->conn, reply); +} + +static DBusHandlerResult am_change_default_headset(struct manager *manager,  +							DBusMessage *msg) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_message(DBusConnection *conn, +					DBusMessage *msg, void *data) +{ +	const char *interface, *member; +	struct manager *manager = data; + +	interface = dbus_message_get_interface(msg); +	member = dbus_message_get_member(msg); + +	if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && +			!strcmp("Introspect", member)) +		return simple_introspect(conn, msg, data); + +	if (strcmp(interface, "org.bluez.audio.Manager") != 0) +		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +	if (strcmp(member, "CreateHeadset") == 0) +		return am_create_headset(manager, msg); + +	if (strcmp(member, "RemoveHeadset") == 0) +		return am_remove_headset(manager, msg); + +	if (strcmp(member, "ListHeadsets") == 0) +		return am_list_headsets(manager, msg); + +	if (strcmp(member, "DefaultHeadset") == 0) +		return am_get_default_headset(manager, msg); + +	if (strcmp(member, "ChangeDefaultHeadset") == 0) +		return am_change_default_headset(manager, msg); + +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const DBusObjectPathVTable am_table = { +	.message_function = am_message, +}; + +static struct manager *audio_manager_new(DBusConnection *conn) +{ +	struct manager *manager; + +	manager = g_new0(struct manager, 1); + +	if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, +						&am_table, manager)) { +		error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); +		g_free(manager); +		return NULL; +	} + +	manager->conn = dbus_connection_ref(conn); + +	return manager; +} + +static void audio_manager_free(struct manager *manager) +{ +	assert(manager != NULL); + +	if (manager->hs_record_id) { +		headset_remove_ag_record(manager->conn, manager->hs_record_id); +		manager->hs_record_id = 0; +	} + +	if (manager->hs_server) { +		g_io_channel_unref(manager->hs_server); +		manager->hs_server = NULL; +	} + +	if (manager->headset_list) { +		g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref, +				manager); +		g_slist_free(manager->headset_list); +		manager->headset_list = NULL; +	} + +	dbus_connection_unref(manager->conn); + +	g_free(manager); +} + +DBusConnection *manager_get_dbus_conn(struct manager *manager) +{ +	return manager->conn; +} +  int audio_init(DBusConnection *conn)  {  	GIOChannel *io; @@ -112,7 +421,13 @@ int audio_init(DBusConnection *conn)  	g_io_channel_unref(io); -	connection = dbus_connection_ref(conn); +	manager = audio_manager_new(conn); +	if (!manager) { +		error("Failed to create an audio manager"); +		return -1; +	} + +	audio_manager_create_headset_server(manager, 12);  	return 0;  } @@ -123,7 +438,7 @@ void audio_exit(void)  	unix_sock = -1; -	dbus_connection_unref(connection); +	audio_manager_free(manager); -	connection = NULL; +	manager = NULL;  } | 
