diff options
| author | Johan Hedberg <johan.hedberg@nokia.com> | 2006-11-24 15:41:08 +0000 | 
|---|---|---|
| committer | Johan Hedberg <johan.hedberg@nokia.com> | 2006-11-24 15:41:08 +0000 | 
| commit | 6834de18b98fea0e7d370a296318115a659047af (patch) | |
| tree | c534bbafc79bc64b486b98454b0baf48807629c9 /audio/headset.c | |
| parent | e8dc0cc060fa03ac83cb8fc04679c79607adc023 (diff) | |
More implementation. E.g. signals and Play and Stop methods
Diffstat (limited to 'audio/headset.c')
| -rw-r--r-- | audio/headset.c | 343 | 
1 files changed, 266 insertions, 77 deletions
| diff --git a/audio/headset.c b/audio/headset.c index 350f5973..4491fc2e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -65,7 +65,7 @@ struct pending_connect {  	GIOChannel *io;  }; -struct hs_connection { +struct headset {  	char address[18];  	GIOChannel *rfcomm; @@ -78,7 +78,7 @@ struct hs_connection {  	int data_length;  }; -static gboolean connect_in_progress = FALSE; +static struct pending_connect *connect_in_progress = NULL;  static uint8_t config_channel = 0; @@ -92,7 +92,7 @@ static DBusConnection *connection = NULL;  static GMainLoop *main_loop = NULL; -static struct hs_connection *connected_hs = NULL; +static struct headset *hs = NULL;  static GIOChannel *server_sk = NULL; @@ -101,6 +101,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,  static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg);  static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg);  static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg); +static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg); +static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg);  static int set_nonblocking(int fd, int *err)  { @@ -140,7 +142,7 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io)  		dbus_connection_unref(c->conn);  	free(c); -	connect_in_progress = FALSE; +	connect_in_progress = NULL;  }  static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, @@ -198,14 +200,14 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg)  static void send_gain_setting(const char *buf)  { +	/* Not yet implemented */  } -static void send_button_press(void) +static void send_simple_signal(const char *name)  {  	DBusMessage *signal; -	signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", -						"AnswerRequested"); +	signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name);  	if (!signal) {  		error("Unable to allocate new AnswerRequested signal");  		return; @@ -228,14 +230,14 @@ static void parse_headset_event(const char *buf, char *rsp, int rsp_len)  	buf += 2;  	if (!strncmp(buf, "+CKPD", 5)) -		send_button_press(); +		send_simple_signal("AnswerRequested");  	else if (!strncmp(buf, "+VG", 3))  		send_gain_setting(buf);  	snprintf(rsp, rsp_len, "\r\nOK\r\n");  } -static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs) +static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)  {  	int sk, ret;  	unsigned char buf[BUF_SIZE]; @@ -252,75 +254,81 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_conn  	ret = read(sk, buf, sizeof(buf) - 1);  	if (ret > 0) { -		int free_space = sizeof(connected_hs->buf) - -					connected_hs->data_start - -					connected_hs->data_length - 1; +		int free_space;  		char *cr; +		free_space = sizeof(hs->buf) - hs->data_start +				- hs->data_length - 1; +  		if (free_space < ret) { +			/* Very likely that the HS is sending us garbage so +			 * just ignore the data and disconnect */  			error("Too much data to fit incomming buffer");  			goto failed;  		} -		memcpy(&connected_hs->buf[connected_hs->data_start], buf, ret); -		connected_hs->data_length += ret; +		memcpy(&hs->buf[hs->data_start], buf, ret); +		hs->data_length += ret;  		/* Make sure the data is null terminated so we can use string  		 * functions */ -		connected_hs->buf[connected_hs->data_length] = '\0'; +		hs->buf[hs->data_length] = '\0'; -		cr = strchr(&connected_hs->buf[connected_hs->data_start], '\r'); +		cr = strchr(&hs->buf[hs->data_start], '\r');  		if (cr) {  			char rsp[BUF_SIZE]; -			int len, written = 0; -			off_t cmd_len = 1 + (off_t) cr - -					(off_t) &connected_hs->buf[connected_hs->data_start]; - +			int len, written; +			off_t cmd_len; +		        +			cmd_len	= 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start];  			*cr = '\0';  			memset(rsp, 0, sizeof(rsp)); -			parse_headset_event(&connected_hs->buf[connected_hs->data_start], +			parse_headset_event(&hs->buf[hs->data_start],  						rsp, sizeof(rsp));  			len = strlen(rsp); +			written = 0;  			while (written < len) {  				int ret;  				ret = write(sk, &rsp[written], len - written);  				if (ret < 0) { -					error("write: %s (%d)", errno, strerror(errno)); +					error("write: %s (%d)", +							strerror(errno), errno);  					break;  				}  				written += ret;  			} -			connected_hs->data_start += cmd_len; -			connected_hs->data_length -= cmd_len; +			hs->data_start += cmd_len; +			hs->data_length -= cmd_len; -			if (!connected_hs->data_length) -				connected_hs->data_start = 0; +			if (!hs->data_length) +				hs->data_start = 0;  		}  	} -	if (connected_hs->ring_timer) { -		g_timeout_remove(connected_hs->ring_timer); -		connected_hs->ring_timer = 0; +	if (hs->ring_timer) { +		g_timeout_remove(hs->ring_timer); +		hs->ring_timer = 0;  	}  	return TRUE;  failed:  	info("Disconnected from %s", hs->address); +	send_simple_signal("Disconnected");  	if (hs->sco)  		g_io_channel_close(hs->sco);  	g_io_channel_close(chan);  	free(hs); -	connected_hs = NULL; +	hs = NULL;  	return FALSE;  } @@ -351,40 +359,110 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)  		return TRUE;  	} -	if (connected_hs || connect_in_progress) { +	if (hs || connect_in_progress) {  		debug("Refusing new connection since one already exists");  		close(cli_sk);  		return TRUE;  	} -	connected_hs = malloc(sizeof(struct hs_connection)); -	if (!connected_hs) { +	hs = malloc(sizeof(struct headset)); +	if (!hs) {  		error("Allocating new hs connection struct failed!");  		close(cli_sk);  		return TRUE;  	} -	memset(connected_hs, 0, sizeof(struct hs_connection)); +	memset(hs, 0, sizeof(struct headset)); -	connected_hs->rfcomm = g_io_channel_unix_new(cli_sk); -	if (!connected_hs->rfcomm) { +	hs->rfcomm = g_io_channel_unix_new(cli_sk); +	if (!hs->rfcomm) {  		error("Allocating new GIOChannel failed!");  		close(cli_sk); -		free(connected_hs); -		connected_hs = NULL; +		free(hs); +		hs = NULL;  		return TRUE;  	} -	ba2str(&addr.rc_bdaddr, connected_hs->address); +	ba2str(&addr.rc_bdaddr, hs->address); + +	debug("Accepted connection from %s", hs->address); + +	send_simple_signal("Connected"); + +	g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, +			hs); + +	return TRUE; +} + +static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +{ +	if (cond & G_IO_NVAL) { +		g_io_channel_unref(chan); +		return FALSE; +	} -	debug("Accepted connection from %s", connected_hs->address); +	if (cond & (G_IO_HUP | G_IO_ERR)) { +		error("Audio connection got disconnected"); +		g_io_channel_close(chan); +		hs->sco = NULL; +		send_simple_signal("Stopped"); +		return FALSE; +	} -	g_io_add_watch(connected_hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, -			connected_hs); +	debug("sco_io_cb: Unhandled IO condition");  	return TRUE;  } +static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, +				struct pending_connect *c) +{ +	int ret, sk, err; +	DBusMessage *reply; +	socklen_t len; + +	if (cond & G_IO_NVAL) { +		g_io_channel_unref(chan); +		return FALSE; +	} + +	sk = g_io_channel_unix_get_fd(chan); + +	len = sizeof(ret); +	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { +		err = errno; +		error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); +		goto failed; +	} + +	if (ret != 0) { +		err = ret; +		error("connect(): %s (%d)", strerror(ret), ret); +		goto failed; +	} + +	hs->sco = chan; +	g_io_add_watch(chan, 0, sco_io_cb, NULL); + +	reply = dbus_message_new_method_return(c->msg); +	if (reply) { +		dbus_connection_send(c->conn, reply, NULL); +		dbus_message_unref(reply); +	} + +	pending_connect_free(c, FALSE); + +	send_simple_signal("Playing"); + +	return FALSE; + +failed: +	err_connect_failed(c->conn, c->msg, err); +	pending_connect_free(c, TRUE); + +	return FALSE; +}  static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c)  { @@ -411,21 +489,23 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe  		goto failed;  	} -	connected_hs = malloc(sizeof(struct hs_connection)); -	if (!connected_hs) { +	hs = malloc(sizeof(struct headset)); +	if (!hs) {  		err = ENOMEM;  		error("Allocating new hs connection struct failed!");  		goto failed;  	} -	memset(connected_hs, 0, sizeof(struct hs_connection)); +	memset(hs, 0, sizeof(struct headset)); -	ba2str(&c->bda, connected_hs->address); -	connected_hs->rfcomm = chan; +	ba2str(&c->bda, hs->address); +	hs->rfcomm = chan; -	debug("Connected to %s", connected_hs->address); +	send_simple_signal("Connected"); -	g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, connected_hs); +	debug("Connected to %s", hs->address); + +	g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs);  	if (c->msg) {  		DBusMessage *reply; @@ -773,13 +853,13 @@ static DBusHandlerResult stop_message(DBusConnection *conn,  	dbus_message_unref(reply); -	if (connected_hs) { -		if (connected_hs->sco) -			g_io_channel_close(connected_hs->sco); -		if (connected_hs->rfcomm) -			g_io_channel_close(connected_hs->rfcomm); -		free(connected_hs); -		connected_hs = NULL; +	if (hs) { +		if (hs->sco) +			g_io_channel_close(hs->sco); +		if (hs->rfcomm) +			g_io_channel_close(hs->rfcomm); +		free(hs); +		hs = NULL;  	}  	if (!config_channel && record_id) { @@ -852,7 +932,11 @@ static DBusHandlerResult hs_message(DBusConnection *conn,  	if (strcmp(member, "CancelRinging") == 0)  		return hs_cancel_ringing(conn, msg); -	/* Handle Headset interface methods here */ +	if (strcmp(member, "Play") == 0) +		return hs_play(conn, msg); + +	if (strcmp(member, "Stop") == 0) +		return hs_stop(conn, msg);  	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;  } @@ -1096,22 +1180,24 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg)  		return DBUS_HANDLER_RESULT_HANDLED;  	} -	if (!connected_hs || strcasecmp(address, connected_hs->address) != 0) +	if (!hs || strcasecmp(address, hs->address) != 0)  		return err_not_connected(conn, msg);  	reply = dbus_message_new_method_return(msg);  	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	if (connected_hs->sco) -		g_io_channel_close(connected_hs->sco); -	if (connected_hs->rfcomm) -		g_io_channel_close(connected_hs->rfcomm); +	if (hs->sco) +		g_io_channel_close(hs->sco); +	if (hs->rfcomm) +		g_io_channel_close(hs->rfcomm); -	info("Disconnected from %s", connected_hs->address); +	info("Disconnected from %s", hs->address); + +	send_simple_signal("Disconnected"); -	free(connected_hs); -	connected_hs = NULL; +	free(hs); +	hs = NULL;  	dbus_connection_send(conn, reply, NULL); @@ -1143,7 +1229,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,  		}  	} -	if (connected_hs) +	if (hs)  		return err_already_connected(conn, msg);  	c = malloc(sizeof(struct pending_connect)); @@ -1152,7 +1238,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,  		return DBUS_HANDLER_RESULT_NEED_MEMORY;  	}        -	connect_in_progress = TRUE; +	connect_in_progress = c;  	memset(c, 0, sizeof(struct pending_connect)); @@ -1192,7 +1278,7 @@ static int send_ring(GIOChannel *io)  	const char *ring_str = "\r\nRING\r\n";  	int sk, written, len; -	sk = g_io_channel_unix_get_fd(connected_hs->rfcomm); +	sk = g_io_channel_unix_get_fd(hs->rfcomm);  	len = strlen(ring_str);  	written = 0; @@ -1213,7 +1299,7 @@ static int send_ring(GIOChannel *io)  static gboolean ring_timer(gpointer user_data)  { -	if (send_ring(connected_hs->rfcomm) < 0) +	if (send_ring(hs->rfcomm) < 0)  		error("Sending RING failed");  	return TRUE; @@ -1223,24 +1309,24 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg)  {  	DBusMessage *reply; -	if (!connected_hs) +	if (!hs)  		return err_not_connected(conn, msg);  	reply = dbus_message_new_method_return(msg);  	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	if (connected_hs->ring_timer) { +	if (hs->ring_timer) {  		debug("Got Ring method call while ringing already in progress");  		goto done;  	} -	if (send_ring(connected_hs->rfcomm) < 0) { +	if (send_ring(hs->rfcomm) < 0) {  		dbus_message_unref(reply);  		return err_failed(conn, msg);  	} -	connected_hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL); +	hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL);  done:  	dbus_connection_send(conn, reply, NULL); @@ -1253,20 +1339,20 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *ms  {  	DBusMessage *reply; -	if (!connected_hs) +	if (!hs)  		return err_not_connected(conn, msg);  	reply = dbus_message_new_method_return(msg);  	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	if (!connected_hs->ring_timer) { +	if (!hs->ring_timer) {  		debug("Got CancelRinging method call but ringing is not in progress");  		goto done;  	} -	g_timeout_remove(connected_hs->ring_timer); -	connected_hs->ring_timer = 0; +	g_timeout_remove(hs->ring_timer); +	hs->ring_timer = 0;  done:  	dbus_connection_send(conn, reply, NULL); @@ -1275,6 +1361,109 @@ done:  	return DBUS_HANDLER_RESULT_HANDLED;  } +static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg) +{ +	struct sockaddr_sco addr; +	struct pending_connect *c; +	int sk, err; + +	if (!hs) +		return err_not_connected(conn, msg); + +	if (hs->sco) +		return err_already_connected(conn, msg); + +	c = malloc(sizeof(struct pending_connect)); +	if (!c) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	memset(c, 0, sizeof(struct pending_connect)); + +	c->conn = dbus_connection_ref(conn); +	c->msg = dbus_message_ref(msg); + +	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(conn, msg, err); +		goto failed; +	} + +	c->io = g_io_channel_unix_new(sk); +	if (!c->io) { +		close(sk); +		pending_connect_free(c, TRUE); +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	} + +	g_io_channel_set_close_on_unref(c->io, TRUE); + +	memset(&addr, 0, sizeof(addr)); +	addr.sco_family = AF_BLUETOOTH; +	bacpy(&addr.sco_bdaddr, BDADDR_ANY); +	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { +		err = errno; +		error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); +		err_connect_failed(conn, msg, err); +		goto failed; +	} + +	if (set_nonblocking(sk, &err) < 0) { +		err_connect_failed(conn, msg, err); +		goto failed; +	} + +	memset(&addr, 0, sizeof(addr)); +	addr.sco_family = AF_BLUETOOTH; +	str2ba(hs->address, &addr.sco_bdaddr); + +	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		if (!(errno == EAGAIN || errno == EINPROGRESS)) { +			err = errno; +			error("connect: %s (%d)", strerror(errno), errno); +			goto failed; +		} + +		debug("Connect in progress"); + +		g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c); +	} else { +		debug("Connect succeeded with first try"); +		sco_connect_cb(c->io, G_IO_OUT, c); +	} + +	return 0; + +failed: +	if (c) +		pending_connect_free(c, TRUE); +	close(sk); +	return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) +{ +	DBusMessage *reply; + +	if (!hs || !hs->sco) +		return err_not_connected(conn, msg); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	g_io_channel_close(hs->sco); +	hs->sco = NULL; + +	send_simple_signal("Stopped"); + +	dbus_connection_send(conn, reply, NULL); +	dbus_message_unref(reply); + +	return DBUS_HANDLER_RESULT_HANDLED; +} +  int main(int argc, char *argv[])  {  	struct sigaction sa; | 
