diff options
| -rw-r--r-- | audio/headset.c | 459 | ||||
| -rw-r--r-- | audio/headset.h | 38 | ||||
| -rw-r--r-- | audio/main.c | 4 | ||||
| -rw-r--r-- | audio/manager.c | 323 | ||||
| -rw-r--r-- | audio/manager.h | 25 | 
5 files changed, 444 insertions, 405 deletions
| diff --git a/audio/headset.c b/audio/headset.c index 44a6ce03..85c233d5 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -51,14 +51,11 @@  #include "dbus.h"  #include "logging.h" - -#define BUF_SIZE 1024 +#include "manager.h" +#include "headset.h"  #define RING_INTERVAL 3000 -#define AUDIO_MANAGER_PATH "/org/bluez/audio" -#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" -  typedef enum {  	HEADSET_EVENT_KEYPRESS,  	HEADSET_EVENT_GAIN, @@ -77,7 +74,6 @@ typedef enum {  struct pending_connect {  	int ch; -	DBusConnection *conn;  	DBusMessage *msg;  	GIOChannel *io;  }; @@ -102,31 +98,10 @@ struct headset {  	headset_state_t state;  	struct pending_connect *pending_connect; -}; -struct manager { -	GIOChannel *server_sk; -	uint32_t record_id; -	int default_hs; -	GSList *headset_list; +	DBusConnection *conn;  }; -static DBusConnection *connection = NULL; - -struct manager *audio_manager_new(DBusConnection *conn); -void audio_manager_free(struct manager *manager); -struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda); -void audio_manager_add_headset(struct manager *manager, struct headset *hs); -gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan); - -struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); -void audio_headset_unref(struct headset *hs); -gboolean audio_headset_close_input(struct headset *hs); -gboolean audio_headset_open_input(struct headset *hs, const char *audio_input); -gboolean audio_headset_close_output(struct headset *hs); -gboolean audio_headset_open_output(struct headset *hs, const char *audio_output); -GIOError audio_headset_send_ring(struct headset *hs); -  static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg);  static void pending_connect_free(struct pending_connect *c) @@ -135,8 +110,6 @@ static void pending_connect_free(struct pending_connect *c)  		g_io_channel_unref(c->io);  	if (c->msg)  		dbus_message_unref(c->msg); -	if (c->conn) -		dbus_connection_unref(c->conn);  	g_free(c);  } @@ -157,13 +130,6 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg,  	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 DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg)  {  	return error_reply(conn, msg, "org.bluez.Error.AlreadyConnected", @@ -226,7 +192,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf)  	dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain,  					DBUS_TYPE_INVALID); -	send_message_and_unref(connection, signal); +	send_message_and_unref(hs->conn, signal);  }  static void hs_signal(struct headset *hs, const char *name) @@ -239,7 +205,7 @@ static void hs_signal(struct headset *hs, const char *name)  		return;  	} -	send_message_and_unref(connection, signal); +	send_message_and_unref(hs->conn, signal);  }  static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len) @@ -398,7 +364,7 @@ static void send_cancel_auth(struct headset *hs)  	dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address,  				DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); -	send_message_and_unref(connection, cancel); +	send_message_and_unref(hs->conn, cancel);  }  static void auth_callback(DBusPendingCall *call, void *data) @@ -435,7 +401,7 @@ static void auth_callback(DBusPendingCall *call, void *data)  	dbus_message_unref(reply);  } -static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, +gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond,  				struct manager *manager)  {  	int srv_sk, cli_sk; @@ -468,16 +434,17 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond,  		return TRUE;  	} -	hs = audio_manager_find_headset_by_bda(manager, &addr.rc_bdaddr); +	hs = manager_find_headset_by_bda(manager, &addr.rc_bdaddr);  	if (!hs) { -		hs = audio_headset_new(connection, &addr.rc_bdaddr); +		hs = audio_headset_new(manager_get_dbus_conn(manager), +					&addr.rc_bdaddr);  		if (!hs) {  			error("Unable to create a new headset object");  			close(cli_sk);  			return TRUE;  		} -		audio_manager_add_headset(manager, hs); +		manager_add_headset(manager, hs);  	}  	if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { @@ -505,7 +472,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond,  	dbus_message_append_args(auth, DBUS_TYPE_STRING, &address,  				DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); -	if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { +	if (dbus_connection_send_with_reply(hs->conn, auth, &pending, -1) == FALSE) {  		error("Sending of authorization request failed");  		goto failed;  	} @@ -655,7 +622,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,  		reply = dbus_message_new_method_return(hs->pending_connect->msg);  		if (reply) -			send_message_and_unref(connection, reply); +			send_message_and_unref(hs->conn, reply);  	}  	/* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ @@ -672,7 +639,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,  failed:  	if (hs->pending_connect) { -		err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err); +		err_connect_failed(hs->conn, hs->pending_connect->msg, err);  		if (hs->pending_connect->io)  			g_io_channel_close(hs->pending_connect->io);  		pending_connect_free(hs->pending_connect); @@ -731,7 +698,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he  			reply = dbus_message_new_method_return(hs->pending_connect->msg);  			if (reply) -				send_message_and_unref(hs->pending_connect->conn, reply); +				send_message_and_unref(hs->conn, reply);  		}  		pending_connect_free(hs->pending_connect); @@ -742,7 +709,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he  failed:  	if (hs->pending_connect) { -		err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err); +		err_connect_failed(hs->conn, hs->pending_connect->msg, err);  		if (hs->pending_connect->io)  			g_io_channel_close(hs->pending_connect->io);  		pending_connect_free(hs->pending_connect); @@ -828,47 +795,11 @@ failed:  	return -1;  } -static int server_socket(uint8_t *channel) +gint headset_bda_cmp(gconstpointer headset, gconstpointer bda)  { -	int sock, lm; -	struct sockaddr_rc addr; -	socklen_t sa_len; - -	sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); -	if (sock < 0) { -		error("server socket: %s (%d)", strerror(errno), errno); -		return -1; -	} - -	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 -1; -	} - -	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 -1; -	} - -	if (listen(sock, 1) < 0) { -		error("server listen: %s", strerror(errno), errno); -		close(sock); -		return -1; -	} - -	sa_len = sizeof(struct sockaddr_rc); -	getsockname(sock, (struct sockaddr *) &addr, &sa_len); -	*channel = addr.rc_channel; +	const struct headset *hs = headset; -	return sock; +	return bacmp(&hs->bda, bda);  }  static int create_ag_record(sdp_buf_t *buf, uint8_t ch) @@ -932,7 +863,7 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch)  	return ret;  } -static uint32_t add_ag_record(uint8_t channel) +uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel)  {  	DBusMessage *msg, *reply;  	DBusError derr; @@ -956,7 +887,7 @@ static uint32_t add_ag_record(uint8_t channel)  				&buf.data, buf.data_size, DBUS_TYPE_INVALID);  	dbus_error_init(&derr); -	reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); +	reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr);  	free(buf.data);  	dbus_message_unref(msg); @@ -984,7 +915,7 @@ static uint32_t add_ag_record(uint8_t channel)  	return rec_id;  } -static int remove_ag_record(uint32_t rec_id) +int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id)  {  	DBusMessage *msg, *reply;  	DBusError derr; @@ -1000,7 +931,7 @@ static int remove_ag_record(uint32_t rec_id)  						DBUS_TYPE_INVALID);  	dbus_error_init(&derr); -	reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); +	reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr);  	dbus_message_unref(msg); @@ -1035,7 +966,7 @@ static void get_record_reply(DBusPendingCall *call, void *data)  	if (dbus_set_error_from_message(&derr, reply)) {  		error("GetRemoteServiceRecord failed: %s", derr.message);  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		dbus_error_free(&derr);  		goto failed;  	} @@ -1045,14 +976,14 @@ static void get_record_reply(DBusPendingCall *call, void *data)  				DBUS_TYPE_INVALID)) {  		error("Unable to get args from GetRecordReply");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	}  	if (!array) {  		error("Unable to get handle array from reply");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	} @@ -1060,7 +991,7 @@ static void get_record_reply(DBusPendingCall *call, void *data)  	if (!record) {  		error("Unable to extract service record from reply");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	} @@ -1078,14 +1009,14 @@ static void get_record_reply(DBusPendingCall *call, void *data)  	if (c->ch == -1) {  		error("Unable to extract RFCOMM channel from service record");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	}  	if (rfcomm_connect(hs, &err) < 0) {  		error("Unable to connect");  		if (c->msg)  -			err_connect_failed(c->conn, c->msg, err); +			err_connect_failed(hs->conn, c->msg, err);  		goto failed;  	} @@ -1109,7 +1040,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)  	DBusMessage *reply = NULL;  	if (!hs || !hs->sco) -		return err_not_connected(connection, msg); +		return err_not_connected(hs->conn, msg);  	if (msg) {  		reply = dbus_message_new_method_return(msg); @@ -1120,8 +1051,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)  	if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) {  		g_io_channel_close(hs->pending_connect->io);  		if (hs->pending_connect->msg) -			err_connect_failed(hs->pending_connect->conn, -						hs->pending_connect->msg, +			err_connect_failed(hs->conn, hs->pending_connect->msg,  						EINTR);  		pending_connect_free(hs->pending_connect);  		hs->pending_connect = NULL; @@ -1131,7 +1061,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg)  	close_sco(hs);  	if (reply) -		send_message_and_unref(connection, reply); +		send_message_and_unref(hs->conn, reply);  	return DBUS_HANDLER_RESULT_HANDLED;  } @@ -1162,8 +1092,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)  		if (hs->pending_connect->io)  			g_io_channel_close(hs->pending_connect->io);  		if (hs->pending_connect->msg) -			err_connect_failed(hs->pending_connect->conn, -						hs->pending_connect->msg, +			err_connect_failed(hs->conn, hs->pending_connect->msg,  						EINTR);  		pending_connect_free(hs->pending_connect);  		hs->pending_connect = NULL; @@ -1180,7 +1109,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg)  	hs->data_length = 0;  	if (reply) -		send_message_and_unref(connection, reply); +		send_message_and_unref(hs->conn, reply);  	return DBUS_HANDLER_RESULT_HANDLED;  } @@ -1204,7 +1133,7 @@ static DBusHandlerResult hs_is_connected(struct headset *hs, DBusMessage *msg)  	dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,  					DBUS_TYPE_INVALID); -	send_message_and_unref(connection, reply); +	send_message_and_unref(hs->conn, reply);  	return DBUS_HANDLER_RESULT_HANDLED;  } @@ -1231,9 +1160,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data)  		error("GetRemoteServiceHandles failed: %s", derr.message);  		if (c->msg) {  			if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) -				err_connect_failed(c->conn, c->msg, EHOSTDOWN); +				err_connect_failed(hs->conn, c->msg, EHOSTDOWN);  			else -				err_not_supported(c->conn, c->msg); +				err_not_supported(hs->conn, c->msg);  		}  		dbus_error_free(&derr);  		goto failed; @@ -1245,21 +1174,21 @@ static void get_handles_reply(DBusPendingCall *call, void *data)  		error("Unable to get args from reply");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	}  	if (!array) {  		error("Unable to get handle array from reply");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	}  	if (array_len < 1) {  		debug("No record handles found");  		if (c->msg)  -			err_not_supported(c->conn, c->msg); +			err_not_supported(hs->conn, c->msg);  		goto failed;  	} @@ -1272,7 +1201,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data)  	if (!msg) {  		error("Unable to allocate new method call");  		if (c->msg)  -			err_connect_failed(c->conn, c->msg, ENOMEM); +			err_connect_failed(hs->conn, c->msg, ENOMEM);  		goto failed;  	} @@ -1284,10 +1213,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data)  					DBUS_TYPE_UINT32, &handle,  					DBUS_TYPE_INVALID); -	if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { +	if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) {  		error("Sending GetRemoteServiceRecord failed");  		if (c->msg)  -			err_connect_failed(c->conn, c->msg, EIO); +			err_connect_failed(hs->conn, c->msg, EIO);  		goto failed;  	} @@ -1327,7 +1256,6 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)  	hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; -	hs->pending_connect->conn = dbus_connection_ref(connection);  	hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL;  	msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", @@ -1347,13 +1275,13 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)  					DBUS_TYPE_STRING, &hs_svc,  					DBUS_TYPE_INVALID); -	if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { +	if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) {  		error("Sending GetRemoteServiceHandles failed");  		pending_connect_free(hs->pending_connect);  		hs->pending_connect = NULL;  		hs->state = HEADSET_STATE_DISCONNECTED;  		dbus_message_unref(msg); -		return err_connect_failed(connection, msg, EIO); +		return err_connect_failed(hs->conn, msg, EIO);  	}  	dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); @@ -1363,7 +1291,7 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg)  	return DBUS_HANDLER_RESULT_HANDLED;;  } -GIOError audio_headset_send_ring(struct headset *hs) +static GIOError audio_headset_send_ring(struct headset *hs)  {  	const char *ring_str = "\r\nRING\r\n";  	GIOError err; @@ -1408,7 +1336,7 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)  	assert(hs != NULL);  	if (hs->state < HEADSET_STATE_CONNECTED) -		return err_not_connected(connection, msg); +		return err_not_connected(hs->conn, msg);  	if (msg) {  		reply = dbus_message_new_method_return(msg); @@ -1423,14 +1351,14 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg)  	if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) {  		dbus_message_unref(reply); -		return err_failed(connection, msg); +		return err_failed(hs->conn, msg);  	}  	hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs);  done:  	if (reply) -		send_message_and_unref(connection, reply); +		send_message_and_unref(hs->conn, reply);  	return DBUS_HANDLER_RESULT_HANDLED;  } @@ -1439,8 +1367,8 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)  {  	DBusMessage *reply = NULL; -	if (!hs) -		return err_not_connected(connection, msg); +	if (hs->state < HEADSET_STATE_CONNECTED) +		return err_not_connected(hs->conn, msg);  	if (msg) {  		reply = dbus_message_new_method_return(msg); @@ -1458,7 +1386,7 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg)  done:  	if (reply) -		send_message_and_unref(connection, reply); +		send_message_and_unref(hs->conn, reply);  	return DBUS_HANDLER_RESULT_HANDLED;  } @@ -1469,17 +1397,14 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)  	struct pending_connect *c;  	int sk, err; -	if (!hs) -		return err_not_connected(connection, msg); -  	if (hs->state < HEADSET_STATE_CONNECTED) -		return err_not_connected(connection, msg); /* FIXME: in progress error? */ +		return err_not_connected(hs->conn, msg); /* FIXME: in progress error? */  	if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) -		return err_already_connected(connection, msg); +		return err_already_connected(hs->conn, msg);  	if (hs->sco) -		return err_already_connected(connection, msg); +		return err_already_connected(hs->conn, msg);  	c = g_try_new0(struct pending_connect, 1);  	if (!c) @@ -1487,14 +1412,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)  	hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; -	c->conn = dbus_connection_ref(connection);  	c->msg = msg ? dbus_message_ref(msg) : NULL;  	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);  	if (sk < 0) {  		err = errno;  		error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); -		err_connect_failed(connection, msg, err); +		err_connect_failed(hs->conn, msg, err);  		goto failed;  	} @@ -1512,13 +1436,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg)  	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {  		err = errno;  		error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); -		err_connect_failed(connection, msg, err); +		err_connect_failed(hs->conn, msg, err);  		goto failed;  	}  	if (set_nonblocking(sk) < 0) {  		err = errno; -		err_connect_failed(connection, msg, err); +		err_connect_failed(hs->conn, msg, err);  		goto failed;  	} @@ -1626,6 +1550,8 @@ struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda)  	bacpy(&hs->bda, bda); +	hs->conn = dbus_connection_ref(conn); +  	return hs;  } @@ -1633,6 +1559,8 @@ void audio_headset_unref(struct headset *hs)  {  	assert(hs != NULL); +	dbus_connection_unref(hs->conn); +  	g_free(hs);  } @@ -1727,266 +1655,7 @@ gboolean audio_headset_open_input(struct headset *hs, const char *input)  	return TRUE;  } -gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan) -{ -	int srv_sk; - -	assert(manager != NULL); - -	if (manager->server_sk) { -		error("Server socket already created"); -		return FALSE; -	} - -	srv_sk = server_socket(&chan); -	if (srv_sk < 0) { -		error("Unable to create server socket"); -		return FALSE; -	} - -	if (!manager->record_id) -		manager->record_id = add_ag_record(chan); - -	if (!manager->record_id) { -		error("Unable to register service record"); -		close(srv_sk); -		return FALSE; -	} - -	manager->server_sk = g_io_channel_unix_new(srv_sk); -	if (!manager->server_sk) { -		error("Unable to allocate new GIOChannel"); -		remove_ag_record(manager->record_id); -		manager->record_id = 0; -		close(srv_sk); -		return FALSE; -	} - -	g_io_add_watch(manager->server_sk, -			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, -			(GIOFunc) server_io_cb, manager); - -	return TRUE; -} - -static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) -{ -	const struct headset *hs = headset; - -	return bacmp(&hs->bda, bda); -} - -struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const 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 audio_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 *audio_headset_get_path(struct headset *hs)  { -	const char *object_path; -	const char *address; -	struct headset *hs; -	bdaddr_t bda; -	DBusMessage *reply; -	DBusError derr; - -	if (!manager) -		return err_not_connected(connection, msg); -	 -	dbus_error_init(&derr); -	if (!dbus_message_get_args(msg, &derr, -					DBUS_TYPE_STRING, &address, -					DBUS_TYPE_INVALID)) { -		err_invalid_args(connection, msg, derr.message); -		return DBUS_HANDLER_RESULT_HANDLED; -	} -	if (dbus_error_is_set(&derr)) { -		err_invalid_args(connection, 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 = audio_manager_find_headset_by_bda(manager, &bda); -	if (!hs) { -		hs = audio_headset_new(connection, &bda); -		if (!hs) -			return error_reply(connection, msg, -					"org.bluez.Error.Failed", -					"Unable to create new headset object"); -		audio_manager_add_headset(manager, hs); -	} - -	object_path = hs->object_path; -	dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, -					DBUS_TYPE_INVALID); - -	return send_message_and_unref(connection, 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; - -	if (!manager) -		return err_not_connected(connection, msg); - -	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(connection, 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, -}; - -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; -	} - -	return manager; -} - -void audio_manager_free(struct manager *manager) -{ -	assert(manager != NULL); - -	if (manager->record_id) { -		remove_ag_record(manager->record_id); -		manager->record_id = 0; -	} - -	if (manager->server_sk) { -		g_io_channel_unref(manager->server_sk); -		manager->server_sk = 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; -	} - -	g_free(manager); -} - -static struct manager *manager = NULL; - -int headset_init(DBusConnection *conn) -{ -	connection = dbus_connection_ref(conn); - -	manager = audio_manager_new(connection); -	if (!manager) { -		error("Failed to create an audio manager"); -		dbus_connection_unref(connection); -		return -1; -	} - -	audio_manager_create_headset_server(manager, 12); - -	return 0; -} - -void headset_exit(void) -{ -	audio_manager_free(manager); - -	manager = NULL; - -	dbus_connection_unref(connection); - -	connection = NULL; +	return hs->object_path;  } diff --git a/audio/headset.h b/audio/headset.h index a87be7e3..3027a9c1 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -20,6 +20,40 @@   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA   *   */ +#ifndef __AUDIO_HEADSET_H +#define __AUDIO_HEADSET_H -int headset_init(DBusConnection *conn); -void headset_exit(void); +#include <bluetooth/bluetooth.h> + +#include <dbus/dbus.h> + +struct headset; + +#include "manager.h" + +#define BUF_SIZE 1024 + +struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); + +void audio_headset_unref(struct headset *hs); + +uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel); + +int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id); + +gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, +				struct manager *manager); + +gint headset_bda_cmp(gconstpointer headset, gconstpointer bda); + +const char *audio_headset_get_path(struct headset *hs); + +gboolean audio_headset_close_output(struct headset *hs); + +gboolean audio_headset_open_output(struct headset *hs, const char *output); + +gboolean audio_headset_close_input(struct headset *hs); + +gboolean audio_headset_open_input(struct headset *hs, const char *input); + +#endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/main.c b/audio/main.c index bf0ddf4d..6ef3b1c6 100644 --- a/audio/main.c +++ b/audio/main.c @@ -75,15 +75,11 @@ int main(int argc, char *argv[])  	audio_init(conn); -	headset_init(conn); -  	if (argc > 1 && !strcmp(argv[1], "-s"))  		register_external_service(conn, "audio", "Audio service", "");  	g_main_loop_run(main_loop); -	headset_exit(); -  	audio_exit();  	dbus_connection_unref(conn); 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;  } diff --git a/audio/manager.h b/audio/manager.h index f97b563a..8335c79a 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -20,6 +20,31 @@   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA   *   */ +#ifndef __AUDIO_MANAGER_H +#define __AUDIO_MANAGER_H + +#include <bluetooth/bluetooth.h> + +#include <dbus/dbus.h> + +struct manager; + +#include "headset.h" + +#define AUDIO_MANAGER_PATH "/org/bluez/audio" + +#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" + +void manager_add_headset(struct manager *manager, struct headset *hs); + +struct headset *manager_find_headset_by_bda(struct manager *manager, +						bdaddr_t *bda); + +DBusConnection *manager_get_dbus_conn(struct manager *manager);  int audio_init(DBusConnection *conn); +  void audio_exit(void); + +#endif /* __AUDIO_MANAGER_H_ */ + | 
